在 Python 中运行以下代码会产生一些意想不到的结果。
print(10000 // 0.1) # prints 99999.0
print(10000 / 0.1) # prints 100000.0
现在,如果两个结果相同,我可能已经理解了差异,因为浮点数是如何以二进制形式存储的。问题是为什么第二个结果与第一个不同?除了后者“地板”结果之外,/ 和 // 的工作方式是否有区别?
在 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 ; 不同之处在于数字的不精确浮点表示,以及 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)
会更好。但是,实施者在编写时可能会想到一些边缘案例或技术细节,//
而我却没有注意到。