1

在 Python 中运行以下代码会产生一些意想不到的结果。

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

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

4

2 回答 2

3

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

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

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

于 2021-11-28T09:50:57.933 回答
1

TLDR ; 不同之处在于数字不精确浮点表示,以及 Cython 的实现与//您预期的略有不同。

//__floordiv____floordiv__(x, y)应该是一样的floor(x / y)。但你已经发现它不是:

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

那么在我看来,你说这是出乎意料的行为是对的。但是为什么会这样呢?

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

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

所以这就是x // y正在做的事情,而不是简单floor(x / y)的 . 但是在我们正在讨论的情况下,x的倍数在哪里y,您可能会期望mod这里会是0,所以div == x / y整个事情都归结为我们真正想要做的事情,即floor(x / y)。然而:

>>> 1.0 % 0.1
0.09999999999999995

因此,在进行模运算时会出现意想不到的结果,最终由 C 标准库函数处理fmod

出错的原因fmod很可能是由于浮点表示和/或算术中的错误。我可以通过为您挑选一些其他示例来说明这一点,所有这些示例都按您的预期工作:

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

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

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

于 2021-11-28T11:03:28.080 回答