我知道在浮点算法中会发生舍入错误,但是有人可以解释这一原因:
>>> 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和Python 3上都发生。
就我所见,这要么是错误,要么是非常愚蠢的规范,//因为我看不出最后一个表达式的求值理由19.0。
//
19.0
为什么a // b不简单定义为floor(a / b)?
a // b
floor(a / b)
编辑 :8.0 % 0.4也计算为0.3999999999999996。至少这是必然的,因为然后8.0 // 0.4 * 0.4 + 8.0 % 0.4评估为8.0
8.0 % 0.4
0.3999999999999996
8.0 // 0.4 * 0.4 + 8.0 % 0.4
8.0
编辑 :这不是浮点数学运算符被破坏吗?因为我在问为什么这个特定的运算会受到(可能是可以避免的)舍入错误,为什么a // b不定义为/等于floor(a / b)
备注 :我想这不起作用的更深层原因是楼层划分是不连续的,因此条件数无限,这使它成为一个不适定的问题。楼层分割和浮点数根本上是不兼容的,并且绝对不要//在浮点数上使用。只需使用整数或分数即可。
正如您和海尔伍德已经注意到的那样,0.4不能完全表示为浮点数。为什么?它是五分之二(4/10 == 2/5),没有有限的二进制分数表示形式。
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。然后执行地板划分,而且因为
Fraction(3602879701896397, 9007199254740992)
19 * Fraction(3602879701896397, 9007199254740992) < 8.0
和
20 * Fraction(3602879701896397, 9007199254740992) > 8.0
结果是19,而不是20。
同样的事情可能发生
8.0 // 0.4
即,似乎地板分割是原子确定的(但仅在解释的float文字的近似float值上)。
那为什么
floor(8.0 / 0.4)
给出“正确”的结果?因为在那里,两个舍入错误彼此抵消。 首先 1)进行除法,得到的东西略小于20.0,但不能表示为float。它四舍五入到最接近的浮点数,恰好是20.0。只有到 那时 ,该floor操作才被执行,但现在 恰好 作用于该操作20.0,因此不再更改该数字。
20.0
floor
1)由于凯尔-斯特兰德指出,该确切的结果确定 则 圆 不是 什么 实际 发生的低2) -电平(CPython中的C代码,甚至CPU指令)。但是,它对于确定预期的3)结果可能是有用的模型。
2)在 最低的 4)级别上,这可能相差不远。一些芯片组通过首先计算更精确的(但仍然不精确,只是具有更多的二进制数字)内部浮点结果,然后舍入为IEEE双精度来确定浮点结果。
3)由Python规范“预期”,不一定是我们的直觉。
4)那么,逻辑门 上方的 最低水平。我们不必考虑使半导体成为可能的量子力学。