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

时间:2015-08-20 16:29:42

标签: 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

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

>>> import math
>>> x = 0.5 
>>> y = 0.1
>>> x / y
5.0
>>> math.floor(x/y)
5.0
>>> x // y
4.0

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

但是,以下类似案例不受影响:

>>> (.5*10) // (.1*10)
5.0
>>> .1 // .1
1.0

4 个答案:

答案 0 :(得分: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的结果在Python中完全 5.0。即使.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 的结果正是5.0。这是因为浮点除法导致精度损失,den.1的差异在此过程中丢失。

这就是为什么math.floor(.5 / .1)可以正常工作的原因:因为.5 / .1 5.0,所以写math.floor(.5 / .1)就是一样的写作math.floor(5.0)

那么为什么没有.5 // .1导致5?

有人可能会认为.5 // .1floor(.5 / .1)的简写,但事实并非如此。事实证明,语义不同。这是PEP says

  

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

的语义
    a // b == floor(a/b)

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

floor((.5 - mod(.5, .1)) / .1)

其中mod.5 / .1的浮点余数向零舍入。通过阅读Python source code来明确这一点。

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

>>> .5 % .1
0.09999999999999998

它是不合理的。由于.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)与[{1}}分歧的原因。在他关于Python历史的blog中,Guido写道:

  

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

a/b = q with remainder r
     

这样

b*q + r = a and 0 <= r < b
     

(假设a和b> = 0)。

现在,Guido假设所有变量都是整数,但如果ab是浮点数,那么这种关系仍将成立,如果 q = a // b。如果q = math.floor(a/b)关系赢得一般保留。所以//可能是首选,因为它满足了这种良好的数学关系。

答案 1 :(得分:6)

那是因为

>>> .1
0.10000000000000001

.1无法用二进制

精确表示

您还可以看到

>>> .5 / 0.10000000000000001
5.0

答案 2 :(得分:2)

问题是Python会将输出四舍五入为described here。由于0.1无法用二进制表示,因此结果类似于4.999999999999999722444243844000。当然,当不使用格式时,这变为5.0

答案 3 :(得分:-1)

  

这是不正确的,我害怕。 .5 / .1完全是5.0。见:(。5 / .1).as_integer_ratio(),得到(5,1)。

是的,5可以表示为5/1,这是真的。但是为了看到Python由于不精确的表示而给出的实际结果的分数,请继续。

首先,导入:

from decimal import *
from fractions import Fraction

易于使用的变量:

// as_integer_ratio() returns a tuple
xa = Decimal((.5).as_integer_ratio()[0])
xb = Decimal((.5).as_integer_ratio()[1])
ya = Decimal((.1).as_integer_ratio()[0])
yb = Decimal((.1).as_integer_ratio()[1])

产生以下数值:

xa = 1
xb = 2
ya = 3602879701896397
yb = 36028797018963968

当然,1/2 == 53602879701896397 / 36028797018963968 == 0.1000000000000000055511151231

那么当我们分裂时会发生什么?

>>> print (xa/xb)/(ya/yb)
4.999999999999999722444243845

但是当我们想要整数比...

>>> print float((xa/xb)/(ya/yb)).as_integer_ratio()
(5, 1)

如前所述,5当然是5/1。这就是Fraction的来源:

>>> print Fraction((xa/xb)/(ya/yb))
999999999999999944488848769/200000000000000000000000000

wolfram alpha确认这确实是4.999999999999999722444243845

为什么不执行Fraction(.5/.1)Fraction(Decimal(.5)/Decimal(.1))

后者会给我们相同的5/1结果。前者会给我们1249999999999999930611060961/250000000000000000000000000。这导致4.999999999999999722444243844,类似但不一样的结果。