Python地板除法中的舍入错误

我知道浮点运算中会发生舍入错误,但有人可以解释一下原因:

>>> 8.0 / 0.4 # as expected
20.0
>>> floor(8.0 / 0.4) # int works too
20
>>> 8.0 // 0.4 # expecting 20.0
19.0
这发生在 x64 上的 Python 2 和 3 上。

据我所知,这要么是一个错误,要么是一个非常愚蠢的规范,//因为我看不出最后一个表达式应该评估为19.0.

为什么a // b不简单地定义为floor(a / b)?

编辑:8.0 % 0.4也评估为0.3999999999999996. 至少这是随之而来的,因为那时8.0 // 0.4 * 0.4 + 8.0 % 0.4评估为8.0

编辑:这不是浮点数学被破坏了吗?因为我在问为什么这个特定的操作会受到(也许可以避免的)舍入错误,以及为什么a // b不定义为 / 等于floor(a / b)

备注:我想这不起作用的更深层原因是楼层划分是不连续的,因此具有无限的条件数,使其成为一个不适定问题。地板除法和浮点数根本不兼容,你永远不应该//在浮点数上使用。只需使用整数或分数。

已邀请:
正如您和 khelwood 已经注意到的那样,0.4不能完全表示为浮点数。为什么?它是五分之二 ( 4/10 == 2/5),它没有有限二进制分数表示。

试试这个:

from fractions import Fraction
Fraction('8.0') // Fraction('0.4')
# or equivalently
# Fraction(8, 1) // Fraction(2, 5)
# or
# Fraction('8/1') // Fraction('2/5')
# 20
然而

Fraction('8') // Fraction(0.4)
# 19
在这里,0.4被解释为一个浮动文本(以及因此浮点二进制数)需要(二进制)舍入,并且只有然后转化为有理数Fraction(3602879701896397, 9007199254740992),这几乎是但不完全4/10。然后执行地板划分,因为

19 * Fraction(3602879701896397, 9007199254740992) < 8.0


20 * Fraction(3602879701896397, 9007199254740992) > 8.0
结果是 19,而不是 20。

同样的情况可能发生在

8.0 // 0.4
即,似乎地板除法是原子确定的(但在解释的浮点文字的唯一近似浮点值上)。

那为什么

floor(8.0 / 0.4)
给出“正确”的结果?因为在那里,两个舍入误差相互抵消。首先1)执行除法,产生的结果略小于 20.0,但不能表示为浮点数。它被四舍五入到最接近的浮点数,恰好是20.0. 只有这样,floor操作才被执行,但现在完全 作用于20.0,因此不再更改数字。

1)由于凯尔-斯特兰德指出,该确切的结果确定则圆不是什么实际发生的低2) -电平(CPython中的C代码,甚至CPU指令)。但是,它可能是确定预期3)结果的有用模型。

2)然而,在最低的4)级别上,这可能不会太远。一些芯片组通过首先计算更精确(但仍然不精确,只是有更多二进制数字)的内部浮点结果然后四舍五入到 IEEE 双精度来确定浮点结果。

3) Python规范“预期”,不一定是我们的直觉。

4)嗯,逻辑门以上的最低级别。我们不必考虑使半导体能够理解这一点的量子力学。

要回复问题请先登录注册