Ice*_*Hay 8 python rounding python-3.x
我知道关于python取整的问题已经被问过多次了,但是答案并没有帮助我。我正在寻找一种将浮点数四舍五入并返回浮点数的方法。该方法还应该接受一个参数,该参数定义要舍入的小数位。我写了一种实现这种舍入的方法。但是,我认为它看起来一点也不优雅。
def round_half_up(number, dec_places):
s = str(number)
d = decimal.Decimal(s).quantize(
decimal.Decimal(10) ** -dec_places,
rounding=decimal.ROUND_HALF_UP)
return float(d)
Run Code Online (Sandbox Code Playgroud)
我不喜欢它,我必须将float转换为字符串(以避免浮点数不准确),然后再使用十进制模块。您有更好的解决方案吗?
编辑:正如下面的答案中指出的那样,我的问题的解决方案不是那么明显,因为正确的舍入首先需要正确表示数字,而float则不是这种情况。所以我希望下面的代码
def round_half_up(number, dec_places):
d = decimal.Decimal(number).quantize(
decimal.Decimal(10) ** -dec_places,
rounding=decimal.ROUND_HALF_UP)
return float(d)
Run Code Online (Sandbox Code Playgroud)
(与上面的代码不同,只是将浮点数直接转换为十进制数而不是首先不转换为字符串)在这样使用时返回2.18:round_half_up(2.175, 2)但这不是因为以浮点数的方式Decimal(2.175)返回Decimal('2.17499999999999982236431605997495353221893310546875')数字由计算机表示。出人意料的是,第一个代码返回2.18,因为浮点数先转换为字符串。似乎str()函数对最初要舍入的数字进行了隐式舍入。因此,发生了两次取整。即使这是我所期望的结果,但从技术上讲还是错误的。
舍入很难做到正确,因为您必须非常小心地处理浮点计算。如果您正在寻找一个优雅的解决方案(简短,易于理解),那么您所喜欢的东西就是一个很好的起点。正确地说,您应该替换decimal.Decimal(str(number))为根据数字本身创建小数,这将为您提供其确切表示形式的小数形式:
d = Decimal(number).quantize(...)...
Run Code Online (Sandbox Code Playgroud)
Decimal(str(number))有效舍入两次,因为将float格式化为字符串表示形式会执行其自身的舍入。这是因为str(float value)不会尝试打印浮点数的完整十进制表示形式,如果将这些确切的数字传递给float构造函数,它将仅打印足够的数字以确保返回相同的浮点数。
如果您想保留正确的舍入,但又避免依赖大型复杂decimal模块,则可以这样做,但是您仍然需要某种方法来实现正确舍入所需的精确算术。例如,您可以使用分数:
import fractions, math
def round_half_up(number, dec_places=0):
sign = math.copysign(1, number)
number_exact = abs(fractions.Fraction(number))
shifted = number_exact * 10**dec_places
shifted_trunc = int(shifted)
if shifted - shifted_trunc >= fractions.Fraction(1, 2):
result = (shifted_trunc + 1) / 10**dec_places
else:
result = shifted_trunc / 10**dec_places
return sign * float(result)
assert round_half_up(1.49) == 1
assert round_half_up(1.5) == 2
assert round_half_up(1.51) == 2
assert round_half_up(2.49) == 2
assert round_half_up(2.5) == 3
assert round_half_up(2.51) == 3
Run Code Online (Sandbox Code Playgroud)
请注意,上述代码中唯一棘手的部分是将浮点数精确转换为分数,并且可以将其卸载到as_integer_ratio()float方法中,这是小数和分数在内部执行的操作。因此,如果您确实想删除对的依赖fractions,则可以将分数运算减少为纯整数运算;您会停留在同一行数内,但会牺牲一些可读性:
def round_half_up(number, dec_places=0):
sign = math.copysign(1, number)
exact = abs(number).as_integer_ratio()
shifted = (exact[0] * 10**dec_places), exact[1]
shifted_trunc = shifted[0] // shifted[1]
difference = (shifted[0] - shifted_trunc * shifted[1]), shifted[1]
if difference[0] * 2 >= difference[1]: # difference >= 1/2
shifted_trunc += 1
return sign * (shifted_trunc / 10**dec_places)
Run Code Online (Sandbox Code Playgroud)
请注意,测试这些功能可让您重点了解创建浮点数时执行的近似处理。例如,进行print(round_half_up(2.175, 2))打印,2.17因为十进制数字2.175不能精确地用二进制表示,因此它被近似小于2.175十进制的近似值代替。函数接收该值,发现它比相应于2.175的小数实际分数小,并决定圆它下来。这不是实现的怪癖;该行为源自浮点数的属性,并且还存在于roundPython 3和2的内置函数中。
| 归档时间: |
|
| 查看次数: |
212 次 |
| 最近记录: |