为什么不同编程语言中0.5 mod 0.1的结果不同?

5

我有一个关于取模的问题。取模操作是通过一个数除以另一个数得到余数。我原本期望 0.5 % 0.1 的结果为 0,但是当我在 PHP 或 .net 中运行时,却得到了 0.1。

我运行的 PHP 代码如下:

var_dump(fmod(0.5, 0.1));

在 .net 中,我尝试了以下方法以获得结果:
Console.WriteLine(0.5%0.1);

我还尝试了一个在线计算器http://www.calculatorpro.com/modulo-calculator/,所有这三种方法都给出了0.1的答案。但是当我在Google中输入此内容时,我得到了我期望的结果http://www.google.nl/search?source=ig&hl=nl&q=0.5%20mod%200.1&meta=。这是.NET/PHP的错误还是Google知道正确的答案?有人能解释为什么存在这些差异吗?

如果你感兴趣的话,在JS中你会得到0.09999999999999998 - JohnP
Python浮点数取模的可能重复问题(虽然语言不同,但问题——浮点误差——是相同的) - user202729
4个回答

4
在PHP、C#、C++、Python或其他编程语言中,当你请求0.1时得到的数字是一个双精度浮点数,意味着它是一个有限的“十进制”——在二进制下有53个有效位,包括第一个1位。实际上,你得到的是最接近0.1的可表示数字,我认为确切的数字是0.1000000000000000055511151231257827021181583404541015625。
另一方面,0.5是一个有限的“二分数”;当你请求这个值时,得到的值将恰好为0.5。
因此,0.5只是比5倍的“0.1”略小一点,因此“0.5 mod 0.1”实际上会给你略小于0.1的结果。实际上,我认为它确切的数字是0.09999999999999997779553950749686919152736663818359375。
现在,当你要求PHP或C#或其他语言显示这个数字时,它将只显示有限数量的数字。你不想让它显示整个可怕的东西。(考虑一下:假设你只要求显示0.1;你想要一个无数位数的怪物,还是想要“0.1”?我知道你的选择。) 而且这个数字实际上非常接近0.1;除非你要求超过15位精度,否则正确的显示应该只是“0.1”。
观察以下Python代码(这是我手头恰好有的):
>>> for n in range(10,20): print (("%%.%dg"%n)%(0.5%0.1))

0.1
0.1
0.1
0.1
0.1
0.1
0.09999999999999998
0.099999999999999978
0.0999999999999999778
0.0999999999999999778

所以:这不是一个错误;也不是浮点数在“精度计算”方面不合适的问题(有时候它是合适的,有时候不是;关键是要理解它在做什么以及你需要什么);是否使用整数取决于你的实际需求,可能会更好。想了解更多关于这个主题的内容,请查看"计算机科学家应该知道的有关浮点算术的所有内容"
至于为什么谷歌的计算器会给出预期的答案0,我不知道。也许他们正在使用十进制算术——真正的基于10的数字——以最小化意外惊喜。(这通常比使用本机浮点数慢得多,但是谷歌拥有大量的CPU可用,并且我敢打赌,他们的搜索处理机器所做的工作只有一小部分与计算器有关。)

3

这不是一个bug,因为模运算也可以定义在浮点数上。在您的PHP代码中,您明确地使用fmod()而不是%,在.NET中,%操作符被定义在每个数字类型上(请查看参考)。


2

"0是整数答案",你的意思是"0是有理数答案"。 - Alin Purcaru

1
如果你想计算0.5%0.1,你可能可以(而且应该)使用整数运算。例如,如果你正在计算涉及货币的某些事情,通常最好将分数作为整数存储,而不是将金额作为浮点数存储。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接