python - 使用整数转换进行舍入是否有不良副作用?

标签 python

如您所见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/

相关文章:

python - 设置或更改新 xarray 维度的值

python - apscheduler 间隔任务未运行

python - 为什么 Sqlalchemy Session.close 不记录 "rollback"?

python - Cpython VM 是否为每个操作码执行 C 代码?

Python,ctypes,多维数组

python - 将字节数组转换为数组中的单个位 [Python 3.5]

python - 按组按最近的较早日期合并数据框

python - 打印用 Python 读入的文件的最后一行

Python Pandas 在列中求和一个常量值如果日期在两个日期之间

python - 从列表序列中提取数据以放入变量中