简体   繁体   English

意外的 integer 除法与浮点除法导致 Python

[英]Unexpected integer division vs. floating-point division result in Python

Running the following code in Python produces a somewhat unexpected result.在 Python 中运行以下代码会产生一些意外的结果。

print(10000 // 0.1)  # prints 99999.0
print(10000 / 0.1)  # prints 100000.0

Now, I might have understood the discrepancy if both results were the same, because of how floating point numbers are stored in binary.现在,如果两个结果相同,我可能已经理解了差异,因为浮点数是如何以二进制形式存储的。 The question is why is the second result different from the first one?问题是为什么第二个结果与第一个不同? Is there a difference in how / and // work besides the latter "flooring" the result?除了后者“地板”结果之外,/ 和 // 的工作方式是否有区别?

// computes the floor of the exact result of the division. //计算除法的确切结果的下限。 / computes the nearest representable float to the exact result of the division. /计算最接近除法结果的可表示浮点数。

For dividend 10000 (exactly 10000) and divisor 0.1 (slightly higher than the real number 0.1 due to floating-point limitations), the exact result is very slightly lower than 100000, but the difference is slight enough that the nearest representable float is 100000.0 .对于被除数10000 (正好 10000)和除数0.1 (由于浮点限制,比实数 0.1 略高),确切的结果比 100000 略低,但差异足够小,最接近的可表示浮点数是100000.0

// produces 99999.0 because the exact result is less than 100000, but / produces 100000.0 rather than something like 99999.99999999999 because 100000.0 is the closest float to the exact result. //产生99999.0因为精确结果小于 100000,但是/产生100000.0而不是99999.99999999999因为100000.0是最接近精确结果的浮点数。

TLDR ; TLDR ; The difference is due both to the inexact floating point representation of the numbers, and the implementation of Cython's // being a little different than you might expect.差异是由于数字不精确浮点表示,以及 Cython 的实现//与您预期的略有不同。

// is __floordiv__ . //__floordiv__ __floordiv__(x, y) is supposed to be the same as floor(x / y) . __floordiv__(x, y)应该与floor(x / y)相同。 But you have already found out its not:但你已经发现它不是:

>>> floor(1.0 / 0.1)
10
>>> 1.0 // 0.1
9.0

In my opinion then, you are right to say this is unexpected behaviour.那么在我看来,你说这是出乎意料的行为是对的。 But why does this happen?但是为什么会这样呢?

If you are using Cython then you can see what // does by reading the C code here .如果您使用的是 Cython,那么您可以通过阅读此处C代码来了解//的作用。 A simple Python implementation of that function that ignores a lot of extra details might look like this:忽略许多额外细节的 function 的简单 Python 实现可能如下所示:

def myfloordiv(x, y):
    mod = x % y
    div = (x - mod) / y
    return float(floor(div))

So this is what x // y is doing, not simply floor(x / y) .所以这就是x // y正在做的事情,而不仅仅是floor(x / y) But in the case we are talking about, where x is a multiple of y , you might expect that mod here will be 0 , so that div == x / y and the whole thing reduces down to what we actually want to do which is floor(x / y) .但是在我们正在讨论的情况下,其中xy的倍数,您可能会期望这里的mod将是0 ,因此div == x / y整个事情减少到我们真正想要做的事情是floor(x / y) However:然而:

>>> 1.0 % 0.1
0.09999999999999995

So, the unexpected result comes in when doing the modulo operation, which is in the end, handled by the C standard library function fmod .因此,在进行模运算时会出现意想不到的结果,最终由 C 标准库 function fmod处理。

The reason fmod gets it wrong is most likely due to errors in floating point representation and/or arithmetic. fmod出错的原因很可能是浮点表示和/或算术错误。 I can illustrate this by picking a few other examples for you, all of which work as you would expect:我可以通过为您挑选一些其他示例来说明这一点,所有这些示例都按您的预期工作:

>>> 100.0 % 0.25
0.0
>>> 100.0 % 0.5
0.0
>>> 100.0 % 1.0
0.0
>>> 100.0 % 2.0
0.0
>>> 100.0 % 4.0
0.0

The pattern is, of course, that all the demonimators are powers of 2, so are representable exactly as floating point numbers, which suggests the errors in the % results are down to floating point respresentation.当然,模式是所有的恶魔都是 2 的幂,因此可以精确地表示为浮点数,这表明%结果中的错误归结为浮点表示。

I still think this behaviour is unexpected.我仍然认为这种行为是出乎意料的。 An implementation of x // y that simply did floor(x, y) would be better as far as I can see.据我所知,简单地执行floor(x, y)x // y的实现会更好。 However, there is likely some edge cases or technical details the implementers had in mind when writing // that I am oblivious to.但是,实施者在编写//时可能会想到一些边缘情况或技术细节,而我却没有注意到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM