我在许多博客文章中都看到了这个错误:http: //atifsiddiqui.blogspot.com/2010/11/windows-calculator-bug.html
这个错误是代码错误还是数学不精确?
我想知道它是否真的是一个错误,它是如何多年来未被发现的?
我应该注意什么以确保它不会在我的自定义计算器程序中发生。
我在许多博客文章中都看到了这个错误:http: //atifsiddiqui.blogspot.com/2010/11/windows-calculator-bug.html
这个错误是代码错误还是数学不精确?
我想知道它是否真的是一个错误,它是如何多年来未被发现的?
我应该注意什么以确保它不会在我的自定义计算器程序中发生。
是的,这是一个错误。它有一个技术解释(外行很难接受)这一事实并不能免除它是一个错误。如果这不是错误,那么您要么争辩——正如我们有时所做的那样——“这是一个特性”,要么是系统的限制。
为了解决这个问题,我建议您将每个结果四舍五入到可接受的精度水平,以消除非常小的错误。正如其他答案所暗示的那样,问题是在您的计算器中,“4”的平方根不是“2”,而是一个非常接近 2 的数字。要解决这一轮结果,请使用小数点后 10、20、30 位或无论你买得起什么。
我认为任何计算器引擎都应该有一个基本的精度水平,该精度水平超过可访问的精度水平足够大的余量,以便用户无法访问浮点运算的限制。如果你走这条路,你会失去一种形式的“准确性”,但你只是说你的计算器精确到 n 位小数。这是完全可以接受的,特别是如果它解决了这个问题。
然而,这并不是什么大问题,不是吗?
顺便说一句,我曾经在一个金融应用程序上工作过,其中供应商提供了一些软件,该软件应该计算一些复合利率。他们的计算总是错误的。他们争辩说这是“由于浮点运算”,并试图在这个问题上对我进行教育;但他们的算法很差。在对美元金额进行复利计算时,我们总是在每次迭代(日、周、月、年或其他任何时间)后对总数进行四舍五入。视情况而定,它可能会四舍五入到最接近的美元、最接近的美分或最接近的 100 美分 - 但这是一个可量化的数量,我们从不每年复利百万分之一美分。如果您想避免本质上是计算舍入误差,这是您应该采取的方法。
正如其他人所说,这不是错误,而是与计算机在内部表示浮点数和处理浮点运算的方式有关。事实证明,你和我不认为浮点数学,但计算机会。并且那个“浮点数”是指二进制点,而不是十进制点。
它返回的数值实际上非常接近 0(我认为我们都同意这是“正确的”十进制答案)。发生的事情是 sqrt 函数本身返回了一个非常接近 2 的数字,但是由于浮点类型的限制,这个数字在内部不能完全存储为2 。输出是数字“2”,因为计算器只是为了显示目的而将其四舍五入,因为知道“2”是您期望的答案。但是当你从 的内部存储的表示中减去 2 时sqrt(4)
,你不会得到正好 0,因为内部存储的数字并不正好是 2。
每个程序员都应该真正阅读“What Every Computer Scientist Should Know About Floating-Point Arithmetic”,它解释了这种行为(特别是“精度”和“二进制到十进制转换”部分)以及许多其他关于浮点运算的令人难以置信的细节。计算机在内部将数字表示为浮点类型的方式。
我同意@Kirk Broadhurst 的观点,这在技术上是一个错误,因为 sqrt(4)-2 的结果严格为 0,而 Calc 给出了不同的(尽管非常接近)结果。通常我们可以忍受这种不精确的事实在这里是无关紧要的。严格来说,程序员应该寻求不同的方法来解决这类问题。
恕我直言,这里很多人没有看到的是 4 和 2 可以用 IEEE 浮点格式精确表示。作为 2 的自然幂,它可以无限精确地表示,因此归咎于 FP 格式的论点也无关紧要。问题来自 sqrt() 函数算法,而不是来自 FP 存储格式。
这是一个常见的浮点问题。如果您以1/3
aqnd 为例,然后乘以3
浮点数,您将不会完全1
返回,但是0.9999999999999999999999
, or 1.000000000000000000001
。Windows 计算器使用了一些算法来尝试最小化我刚才解释的情况,但可能是他们没有处理所有特殊情况......
它不完全是一个错误,更多的是一个可用性问题,因为他们处理了一些情况,但不是全部..
这不是错误(计算机以这种方式工作!)。SO上有很多关于这个主题的问题。例如,搜索“JavaScript math broken”。
有经验的计算机用户还应该认识到 -8.1648465955514287168521180122928e-39 实际上与零相同。
如果您想避免这样的事情,您可以在将每个结果转换为字符串时对其进行四舍五入。-8.1648465955514287168521180122928e-39 将四舍五入为 0。但是,如果您正在编写一个非常高级的计算器,这将不起作用,例如可以使用普朗克常数(如果您这样做,普朗克常数将被视为等于零,这是不好的)。一个很好的选择是使用符号数学,但是写一个计算器不需要几分钟,但是几个月/几年......
它比正常的浮点问题稍微复杂一些,因为 calc 实际上使用任意精度数学。至关重要的是,正如raymond chen所说,它似乎只对基本操作使用无限精度
今天,Calc 的内部计算以无限精度完成基本运算(加法、减法、乘法、除法)和 32 位精度的高级运算(平方根、超越运算符)。
所以推测平方根实际上导致的值非常接近,但不是 2,而是显示为 2,在精确减法之后,你留下一个非常接近 0 的数字,它不显示为 0,虽然这是一个错误? 依靠。
通过纯粹的猜测,我会说这是因为计算器并不能完全得到2
平方根的结果(取决于它如何计算根)。但是当结果远离零时,它只是将显示四舍五入。但是当数字接近零时,它显示了确切的结果。
对于自己的计算器,您可能不会因为没有如此高的准确度而得到这样的结果(在使用编程语言提供的普通数学功能时通常没有这种结果)。