为什么在Python中两个均匀可分的浮点数为math.floor(x / y)!= x // y? [英] Why is math.floor(x/y) != x // y for two evenly divisible floats in Python?

查看:187
本文介绍了为什么在Python中两个均匀可分的浮点数为math.floor(x / y)!= x // y?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have been reading about division and integer division in Python and the differences between division in Python2 vs Python3. For the most part it all makes sense. Python 2 uses integer division only when both values are integers. Python 3 always performs true division. Python 2.2+ introduced the // operator for integer division.

其他程序员提供的示例使操作更简洁,例如:

Examples other programmers have offered work out nice and neat, such as:

>>> 1.0 // 2.0      # floors result, returns float
0.0
>>> -1 // 2         # negatives are still floored
-1

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

How is // implemented? Why does the following happen:

>>> 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 吗?

Shouldn't x // y = math.floor(x/y)? These results were produced on python2.7, but since x and y are both floats the results should be the same on python3+. If there is some floating point error where x/y is actually 4.999999999999999 and math.floor(4.999999999999999) == 4.0 wouldn't that be reflected in x/y?

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

The following similar cases, however, aren't affected:

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


推荐答案

我没有找到其他令人满意的答案。当然, .1 没有有限的二进制扩展,所以我们的直觉是表示错误是元凶。但是仅凭这种直觉并不能真正解释为什么 math.floor(.5 / .1)产生 5.0 .5 // .1 产生 4.0

I didn't find the other answers satisfying. Sure, .1 has no finite binary expansion, so our hunch is that representation error is the culprit. But that hunch alone doesn't really explain why math.floor(.5/.1) yields 5.0 while .5 // .1 yields 4.0.

重点是 a // b 实际上是实际上正在做 floor((a-(a%b))/ b) ,而不是简单的 floor(a / b)

The punchline is that a // b is actually doing floor((a - (a % b))/b), as opposed to simply floor(a/b).

首先,请注意 .5 / .1 在Python中完全是 5.0 。即使 .1 不能精确表示,也是如此。以下面的代码为例:

First of all, note that the result of .5 / .1 is exactly 5.0 in Python. This is the case even though .1 cannot be exactly represented. Take this code, for instance:

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 的差为

This shows that .5 can be represented with a finite binary expansion, but .1 cannot. But it also shows that despite this, the result of .5 / .1 is exactly 5.0. This is because floating point division results in the loss of precision, and the amount by which den differs from .1 is lost in the process.

这就是为什么 math.floor(.5 / .1)可以按预期工作的原因:因为 .5 / .1 5.0 ,所以写 math.floor(.5 / .1)与编写 math.floor(5.0)相同。

That's why math.floor(.5 / .1) works as you might expect: since .5 / .1 is 5.0, writing math.floor(.5 / .1) is just the same as writing math.floor(5.0).

可能会假设 .5 // .1 floor(.5 / .1)的简写,但是不是这种情况。事实证明,语义是不同的。即使 PEP说

One might assume that .5 // .1 is shorthand for floor(.5 / .1), but this is not the case. As it turns out, the semantics differ. This is even though the PEP says:


地板除法将在所有Python数字
类型中实现,并具有以下语义:

Floor division will be implemented in all the Python numeric types, and will have the semantics of

    a // b == floor(a/b)


事实证明, .5 // //。1 的语义实际上等于: p>

As it turns out, the semantics of .5 // .1 are actually equivalent to:

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

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

where mod is the floating point remainder of .5 / .1 rounded towards zero. This is made clear by reading the Python source code.

.1 不能准确表示为的事实二进制扩展会导致此问题。 .5 / .1 的浮点余数为零:

This is where the fact that .1 can't be exactly represented by binary expansion causes the problem. The floating point remainder of .5 / .1 is not zero:

>>> .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

and it makes sense that it isn't. Since the binary expansion of .1 is ever-so-slightly greater than the actual decimal .1, the largest integer alpha such that alpha * .1 <= .5 (in our finite precision math) is alpha = 4. So mod(.5, .1) is nonzero, and is roughly .1. Hence floor((.5 - mod(.5, .1)) / .1) becomes floor((.5 - .1) / .1) becomes floor(.4 / .1) which equals 4.

这就是为什么 .5 // .1 == 4

a的行为// b 似乎很奇怪,但有一个原因使其与 math.floor(a / b)背道而驰。在他的博客中Python,Guido写道:

The behavior of a // b may seem strange, but there's a reason for it's divergence from math.floor(a/b). In his blog on the history of Python, Guido writes:


整数除法运算(//)及其同级运算,即
模运算(%),在一起并满足良好的数学
关系(所有变量均为整数):

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

使得

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

(假设a和b> = 0)。

(assuming a and b are >= 0).

现在,Guido假定所有变量都是整数,但是如果 a b 是浮点数, if q = a // b 。如果 q = math.floor(a / b),则关系不会成立。因此, // 可能是首选,因为它满足了这种良好的数学关系。

Now, Guido assumes that all variables are integers, but that relationship will still hold if a and b are floats, if q = a // b. If q = math.floor(a/b) the relationship won't hold in general. And so // might be preferred because it satisfies this nice mathematical relationship.

这篇关于为什么在Python中两个均匀可分的浮点数为math.floor(x / y)!= x // y?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆