意外的整数除法与浮点除法结果 Python

Unexpected integer division vs. floating-point division result in Python

运行 Python 中的以下代码产生了有点意外的结果。

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

现在,如果两个结果相同,我可能已经理解了差异,因为浮点数是如何以二进制形式存储的。问题是为什么第二个结果和第一个不同?除了后者“降低”结果之外,/ 和 // 的工作方式有区别吗?

// 计算除法的确切结果的底数。 / 计算最接近除法精确结果的可表示浮点数。

对于被除数10000(恰好10000)和除数0.1(由于浮点数限制,略高于实数0.1),精确结果略低于100000,但是差异很小,最接近的可表示浮点数是 100000.0.

// 产生 99999.0 因为确切的结果小于 100000,但是 / 产生 100000.0 而不是类似 99999.99999999999 的东西,因为 100000.0 是最接近精确结果的浮点数。

TLDR;差异是由于 both 数字的不精确浮点表示,以及 Cython 的实现 // 与您预期的略有不同。

//__floordiv____floordiv__(x, y) 应该与 floor(x / y) 相同。但是你已经发现它不是:

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

那么在我看来,您说这是意外行为是对的。但是为什么会这样呢?

如果您使用的是 Cython,那么您可以通过阅读 C 代码 here 来了解 // 的作用。忽略大量额外细节的该函数的简单 Python 实现可能如下所示:

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

所以这就是 x // y 正在做的事情,而不仅仅是 floor(x / y)。但是在我们讨论的情况下,xy 的倍数,您可能会认为这里的 mod 将是 0,因此 div == x / y整个事情归结为我们真正想做的事情 floor(x / y)。然而:

>>> 1.0 % 0.1
0.09999999999999995

所以,在做取模运算时出现了意想不到的结果,最终由C标准库函数处理fmod

fmod 出错的原因很可能是由于浮点表示法 and/or 运算错误。我可以通过为您挑选一些其他示例来说明这一点,所有这些示例都可以正常工作:

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

当然,模式是所有 demonimators 都是 2 的幂,因此可以精确表示为浮点数,这表明 % 结果中的错误归结为浮点表示。

我仍然认为这种行为是出乎意料的。据我所知,仅执行 floor(x, y)x // y 的实现会更好。但是,在编写 // 时,实施者可能会考虑到一些边缘情况或技术细节,而我却没有注意到。