在 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)会更好。但是,实施者在编写时可能会想到一些边缘案例或技术细节,//而我却没有注意到。