我首先在 Python 中遇到了这种现象,但结果证明这是常见的答案,例如 MS Excel 给出了这个。 Wolfram Alpha 给出了一个有趣的 split 样答案,它指出零的有理近似为 1/5。 ( 1.0 mod 0.1 )
另一方面,如果我手动实现定义,它会给我“正确”的答案(0)。
def myFmod(a,n):
return a - floor(a/n) * n
这里发生了什么。我错过了什么吗?
最佳答案
因为 0.1
不是 0.1;该值不能用 double 表示,因此它会四舍五入到最接近的 double 数,即:
0.1000000000000000055511151231257827021181583404541015625
当您调用
fmod
时,您会得到除以上面列出的值的余数,这正是:0.0999999999999999500399638918679556809365749359130859375
打印时四舍五入为
0.1
(或者可能是 0.09999999999999995
)。换句话说,
fmod
工作得很好,但你没有给它你认为的输入。编辑: 您自己的实现为您提供了正确答案,因为它不太准确,信不信由你。首先,请注意
fmod
计算余数时没有任何舍入误差;不准确的唯一来源是使用值 0.1
引入的表示错误。现在,让我们来看看您的实现,看看它产生的舍入误差是如何完全抵消表示误差的。一次评估
a - floor(a/n) * n
,跟踪每个阶段计算的确切值:首先我们评估
1.0/n
,其中 n
是最接近 0.1
的 double 近似值,如上所示。这种划分的结果大约是:9.999999999999999444888487687421760603063276150363492645647081359...
请注意,此值不是可表示的 double 数——因此它会四舍五入。要了解这种舍入是如何发生的,让我们看看二进制而不是十进制的数字:
1001.1111111111111111111111111111111111111111111111111 10110000000...
空格表示四舍五入到 double 发生的位置。由于圆点之后的部分大于精确的中间点,因此该值向上取整为
10
。可以预见,
floor(10.0)
是 10.0
。所以剩下的就是计算 1.0 - 10.0*0.1
。在二进制中,
10.0 * 0.1
的确切值是:1.0000000000000000000000000000000000000000000000000000 0100
同样,该值不能表示为 double 值,因此在空格指示的位置四舍五入。这次它精确地四舍五入到
1.0
,所以最终的计算是 1.0 - 1.0
,当然是 0.0
。您的实现包含两个舍入错误,在这种情况下,它们恰好恰好抵消了
0.1
值的表示错误。相比之下, fmod
始终是精确的(至少在具有良好数字库的平台上),并且会暴露 0.1
的表示错误。关于python - 为什么 fmod(1.0,0.1) == .1?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4218961/