为什么math.floor(x/y)!= x // y用于Python中两个可分割的浮点数?

J.M*_*ney 23 python division integer-division

我一直在阅读Python中的除法和整数除法以及Python2与Python3之间的区别.在大多数情况下,这一切都是有道理的.仅当两个值都是整数时,Python 2才使用整数除法.Python 3总是执行真正的划分.Python 2.2+引入了//整数除法运算符.

其他程序员提供的示例很好,很整洁,例如:

>>> 1.0 // 2.0      # floors result, returns float
0.0
>>> -1 // 2         # negatives are still floored
-1
Run Code Online (Sandbox Code Playgroud)

如何//实施?为什么会发生以下情况:

>>> import math
>>> x = 0.5 
>>> y = 0.1
>>> x / y
5.0
>>> math.floor(x/y)
5.0
>>> x // y
4.0
Run Code Online (Sandbox Code Playgroud)

不应该x // y = math.floor(x/y)?这些结果是在python2.7上生成的,但由于x和y都是浮点数,因此python3 +上的结果应该相同.如果有一些浮点错误x/y实际上4.999999999999999并且math.floor(4.999999999999999) == 4.0不会反映出来x/y

但是,以下类似情况不受影响:

>>> (.5*10) // (.1*10)
5.0
>>> .1 // .1
1.0
Run Code Online (Sandbox Code Playgroud)

jme*_*jme 24

我没有找到满意的其他答案.当然,.1没有有限的二进制扩展,所以我们的预感是表示错误是罪魁祸首.但这种预感并不能真正解释为什么math.floor(.5/.1)收益率5.0.5 // .1收益率4.0.

与简单相反,实际上a // b就是这样做的妙语.floor((a - (a % b))/b)floor(a/b)

.5/.1 正好是 5.0

首先,请注意,结果.5 / .1正好 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)
Run Code Online (Sandbox Code Playgroud)

和相应的输出:

num:  0.5
den:  0.1000000000000000055511151231257827021181583404541015625
res:  5
Run Code Online (Sandbox Code Playgroud)

这表明.5可以用有限的二进制扩展来表示,但.1不能.但它也表明,尽管这样,结果.5 / .1 完全相同5.0.这是因为浮点除法导致精度损失,并且在该过程中丢失了den与之不同的量.1.

这就是为什么math.floor(.5 / .1)你可能期望的工作原理:因为.5 / .1 就是 5.0,写作math.floor(.5 / .1)是一样的写作math.floor(5.0).

那为什么不.5 // .1导致5?

人们可能会认为这.5 // .1是速记floor(.5 / .1),但事实并非如此.事实证明,语义不同.即使PEP说:

分区将在所有Python数字类型中实现,并具有语义

    a // b == floor(a/b)
Run Code Online (Sandbox Code Playgroud)

事实证明,语义.5 // .1实际上等同于:

floor((.5 - mod(.5, .1)) / .1)
Run Code Online (Sandbox Code Playgroud)

其中mod浮点余数.5 / .1向零舍入.通过阅读Python源代码可以清楚地看到这一点.

.1二进制扩展无法准确表示的事实导致问题.的浮点余数.5 / .1不是零:

>>> .5 % .1
0.09999999999999998
Run Code Online (Sandbox Code Playgroud)

而事实并非如此.由于二元扩张.1比实际小数不断所谓稍大.1,最大的整数alpha使得alpha * .1 <= .5(我们有限精度数学)是alpha = 4.所以mod(.5, .1)非零,大概是.1.因此floor((.5 - mod(.5, .1)) / .1)变得floor((.5 - .1) / .1)变成floor(.4 / .1)相当于4.

这就是原因.5 // .1 == 4.

为什么这样//做?

这种行为a // b可能看起来很奇怪,但有一个原因就是它的分歧math.floor(a/b).在他关于Python历史的博客中,Guido写道:

整数除法运算(//)及其兄弟,模运算(%),一起使用并满足一个很好的数学关系(所有变量都是整数):

a/b = q with remainder r
Run Code Online (Sandbox Code Playgroud)

这样的

b*q + r = a and 0 <= r < b
Run Code Online (Sandbox Code Playgroud)

(假设a和b> = 0).

现在,圭多假定所有变量均为整数,但这种关系将仍然坚持,如果ab是花车,如果 q = a // b.如果q = math.floor(a/b)这种关系一般不会成立.因此它//可能是首选,因为它满足了这种良好的数学关系.


njz*_*zk2 6

那是因为

>>> .1
0.10000000000000001
Run Code Online (Sandbox Code Playgroud)

.1 无法用二进制精确表示

你也可以看到

>>> .5 / 0.10000000000000001
5.0
Run Code Online (Sandbox Code Playgroud)

  • 说别人发布了正确的答案,并没有让你的回答正确.它使他们的答案正确,并使你的答案不完整更明显.看,你可以坐在这里,在评论中为此哭泣,因为我打电话给你,你明显对此采取了冒犯,或者把我提供给你,然后解决你的答案.编辑:另外,他的答案也不完整,因为这不是特定于python的问题.它影响任何base 2 cpu(我不知道非base2 cpu) (3认同)