为什么 math.floor(x/y) != x // y 对于 Python 中的两个可整除的浮点数?
Why is math.floor(x/y) != x // y for two evenly divisible floats in Python?
我一直在阅读 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.999999999999999
而 math.floor(4.999999999999999) == 4.0
不会反映在 x/y
中吗?
但是,以下类似情况不受影响:
>>> (.5*10) // (.1*10)
5.0
>>> .1 // .1
1.0
那是因为
>>> .1
0.10000000000000001
.1
无法用二进制精确表示
你也可以看到
>>> .5 / 0.10000000000000001
5.0
问题是 Python 会将输出四舍五入为 described here。由于 0.1
不能精确地用二进制表示,因此结果类似于 4.999999999999999722444243844000
。当不使用格式时,自然会变成 5.0
。
我没有发现其他答案令人满意。当然,.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)
以及对应的输出:
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 // .1
是 shorthand 对应 floor(.5 / .1)
,但事实并非如此。事实证明,语义不同。即使 PEP says:
Floor division will be implemented in all the Python numeric
types, and will have the semantics of
a // b == floor(a/b)
事实证明,.5 // .1
的语义 实际上 等同于:
floor((.5 - mod(.5, .1)) / .1)
其中 mod
是 .5 / .1
的浮点余数,四舍五入为零。通过阅读 Python source code.
可以清楚地了解这一点
This 是 .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)
的差异是有原因的。 Guido 在他关于 [=126= 的历史的 blog 中写道:
The integer division operation (//) and its sibling, the modulo
operation (%), go together and satisfy a nice mathematical
relationship (all variables are integers):
a/b = q with remainder r
such that
b*q + r = a and 0 <= r < b
(assuming a and b are >= 0).
现在,Guido 假设所有变量都是整数,但如果 a
和 b
是浮点数,这种关系仍然成立,if q = a // b
.如果 q = math.floor(a/b)
关系 一般不会 成立。所以 //
可能是首选,因为它满足这个很好的数学关系。
This isn't correct, I'm afraid. .5 / .1 is 5.0 exactly. See: (.5/.1).as_integer_ratio(), which yields (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 == 5
和3602879701896397 / 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
,相似但不相同的结果。
我一直在阅读 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.999999999999999
而 math.floor(4.999999999999999) == 4.0
不会反映在 x/y
中吗?
但是,以下类似情况不受影响:
>>> (.5*10) // (.1*10)
5.0
>>> .1 // .1
1.0
那是因为
>>> .1
0.10000000000000001
.1
无法用二进制精确表示
你也可以看到
>>> .5 / 0.10000000000000001
5.0
问题是 Python 会将输出四舍五入为 described here。由于 0.1
不能精确地用二进制表示,因此结果类似于 4.999999999999999722444243844000
。当不使用格式时,自然会变成 5.0
。
我没有发现其他答案令人满意。当然,.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)
以及对应的输出:
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 // .1
是 shorthand 对应 floor(.5 / .1)
,但事实并非如此。事实证明,语义不同。即使 PEP says:
Floor division will be implemented in all the Python numeric types, and will have the semantics of
a // b == floor(a/b)
事实证明,.5 // .1
的语义 实际上 等同于:
floor((.5 - mod(.5, .1)) / .1)
其中 mod
是 .5 / .1
的浮点余数,四舍五入为零。通过阅读 Python source code.
This 是 .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)
的差异是有原因的。 Guido 在他关于 [=126= 的历史的 blog 中写道:
The integer division operation (//) and its sibling, the modulo operation (%), go together and satisfy a nice mathematical relationship (all variables are integers):
a/b = q with remainder r
such that
b*q + r = a and 0 <= r < b
(assuming a and b are >= 0).
现在,Guido 假设所有变量都是整数,但如果 a
和 b
是浮点数,这种关系仍然成立,if q = a // b
.如果 q = math.floor(a/b)
关系 一般不会 成立。所以 //
可能是首选,因为它满足这个很好的数学关系。
This isn't correct, I'm afraid. .5 / .1 is 5.0 exactly. See: (.5/.1).as_integer_ratio(), which yields (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 == 5
和3602879701896397 / 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
,相似但不相同的结果。