我没有发现其他答案令人满意。当然,.1
没有有限的二进制展开式,所以我们的预感是表示错误是罪魁祸首。但这种预感本身并不能真正解释原因math.floor(.5/.1)
yields 5.0
while .5 // .1
yields 4.0
.
重点是a // b
is actually doing floor((a - (a % b))/b)
,而不是简单地floor(a/b)
.
.5 / .1 是exactly 5.0
首先,请注意,结果.5 / .1
is exactly 5.0
在Python中。情况确实如此,尽管.1
无法准确表示。以这段代码为例:
from decimal import Decimal
num = Decimal(.5)
den = Decimal(.1)
res = Decimal(.5/.1)
print('num: ', num)
print('den: ', den)
print('res: ', res)
以及相应的输出:
num: 0.5
den: 0.1000000000000000055511151231257827021181583404541015625
res: 5
这表明.5
可以用有限二进制展开来表示,但是.1
不能。但这也表明,尽管如此,结果.5 / .1
is确切地5.0
。这是因为浮点除法会导致精度损失,并且损失的量den
不同于.1
就在这个过程中丢失了。
这就是为什么math.floor(.5 / .1)
正如您所期望的那样工作:因为.5 / .1
is 5.0
, 写作math.floor(.5 / .1)
和写作一样math.floor(5.0)
.
那么为什么不呢.5 // .1
结果是5?
人们可能会认为.5 // .1
是简写floor(.5 / .1)
, 但这种情况并非如此。事实证明,语义不同。这即使是PEP says https://www.python.org/dev/peps/pep-0238/:
楼层除法将在所有Python数值中实现
类型,并且将具有以下语义
a // b == floor(a/b)
事实证明,语义.5 // .1
are actually相当于:
floor((.5 - mod(.5, .1)) / .1)
where mod
是浮点余数.5 / .1
四舍五入到零。通过阅读以下内容可以清楚地了解这一点Python源代码 https://github.com/python/cpython/blob/829b49cbd2e4b1d573470da79ca844b730120f3d/Objects/floatobject.c#L578.
This事实是.1
无法用二进制展开来精确表示会导致问题。的浮点余数.5 / .1
is not zero:
>>> .5 % .1
0.09999999999999998
事实并非如此,这是有道理的。由于二进制展开.1
比实际的小数稍大一些.1
, 最大整数alpha
这样alpha * .1 <= .5
(在我们的有限精度数学中)是alpha = 4
. So mod(.5, .1)
是非零的,并且大约是.1
. Hence floor((.5 - mod(.5, .1)) / .1)
变成floor((.5 - .1) / .1)
变成floor(.4 / .1)
这等于4
.
这就是为什么.5 // .1 == 4
.
为什么//
去做?
的行为a // b
可能看起来很奇怪,但它与math.floor(a/b)
。在他的blog http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html关于 Python 的历史,Guido 写道:
整数除法运算 (//) 及其兄弟,取模
运算(%),一起满足一个很好的数学
关系(所有变量均为整数):
a/b = q with remainder r
这样
b*q + r = a and 0 <= r < b
(假设 a 和 b >= 0)。
现在,Guido 假设所有变量都是整数,但是如果a
and b
是浮标,if q = a // b
. If q = math.floor(a/b)
关系won't一般情况下持有。所以//
可能是首选,因为它满足这种良好的数学关系。