如您所见here ,Python(和 Java 等)中的舍入不应轻率进行。
如果你想像在学校学到的那样进行舍入,你不应该这样做:
>>> round(20.5)
20
要四舍五入“学校式”,通常您会使用十进制方法:
>>> import decimal
>>> decimal.Decimal(20.5).quantize(1, rounding=decimal.ROUND_HALF_UP)
Decimal('21')
在我看来,这不是Pythonic,我永远无法记住它。
另一种选择是:
>>> int(20.5 + 0.5)
21
如果您想四舍五入到逗号后的特定部分,请执行以下操作:
>>> int(20.5555555555 * 1000 + 0.5) / 1000
20.556
这种舍入方式会产生一些不好的副作用吗?
最佳答案
你所描述的(几乎)是round half up战略。但是使用 int
它不适用于负数:
>>> def round_half_up(x, n=0):
... shift = 10 ** n
... return int(x*shift + 0.5) / shift
...
>>> round_half_up(-1.26, 1)
-1.2
相反,您应该使用 math.floor
才能正确处理负数:
>>> import math
>>>
>>> def round_half_up(x, n=0):
... shift = 10 ** n
... return math.floor(x*shift + 0.5) / shift
...
>>> round_half_up(-1.26, 1)
-1.3
该策略的缺点是它往往会扭曲一组数字的统计数据,例如平均值或标准差。假设您收集了一些数字,并且所有数字都以 .5
结尾;然后将它们分别向上舍入将明显增加平均值:
>>> numbers = [-3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5]
>>> N = len(numbers)
>>> sum(numbers) / N
0.0
>>> sum(round_half_up(x) for x in numbers) / N
0.5
如果我们使用策略round half to even相反,这将导致一些数字向上舍入,而其他数字向下舍入,从而相互补偿:
>>> sum(round(x) for x in numbers) / N
0.0
如您所见,例如,平均值保持不变。
当然,只有当数字均匀分布时,这才有效。如果存在偏爱 odd + 0.5
形式的数字的趋势,那么此策略也无法防止偏差:
>>> numbers = [i + 0.5 for i in range(-3, 3, 2)]
>>> N = len(numbers)
>>> sum(numbers) / N
-0.5
>>> sum(round_half_up(x) for x in numbers) / N
0.0
>>> sum(round(x) for x in numbers) / N
0.0
对于这组数字,round
实际上是“四舍五入”,因此两种方法都存在相同的偏差。
正如您所看到的,舍入策略明显影响了平均值等多项统计数据的偏差。 “将一半舍入到偶数”往往会消除这种偏差,但显然更倾向于偶数而不是奇数,因此也会扭曲原始分布。
关于float
对象的注释
由于浮点精度有限,这种“四舍五入”算法也可能会产生一些意想不到的惊喜:
>>> round_half_up(-1.225, 2)
-1.23
将 -1.225
解释为十进制数,我们期望结果为 -1.22
。我们得到 -1.23
,因为 round_half_up
中的中间 float 略高于其预期值:
>>> f'{-1.225 * 100 + 0.5:.20f}'
'-122.00000000000001421085'
floor
得到的数字为 -123
(如果我们得到 -122.0
,则不是 -122
) >之前)。这是由于浮点错误造成的,并且首先是 -1.225
实际上不是以 -1.225
形式存储在内存中,而是作为一个稍微小一点的数字存储。因此,使用 Decimal
是在所有情况下获得正确舍入的唯一方法。
关于python - 使用整数转换进行舍入是否有不良副作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57834462/