意外的整数除法与浮点除法结果 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)
。但是在我们讨论的情况下,x
是 y
的倍数,您可能会认为这里的 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
的实现会更好。但是,在编写 //
时,实施者可能会考虑到一些边缘情况或技术细节,而我却没有注意到。
运行 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)
。但是在我们讨论的情况下,x
是 y
的倍数,您可能会认为这里的 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
的实现会更好。但是,在编写 //
时,实施者可能会考虑到一些边缘情况或技术细节,而我却没有注意到。