不同编程语言的浮点数学

时间:2014-02-19 06:34:04

标签: python r haskell floating-point julia

我知道浮点数学最多可能是丑陋的,但我想知道是否有人可以解释以下的怪癖。在我测试的大多数编程语言中,0.4到0.2的加法给出了轻微的误差,其中0.4 + 0.1 + 0.1给出了非。

两种计算不平等的原因是什么,以及在各自的编程语言中可以采取哪些措施来获得正确的结果。

在python2 / 3中

.4 + .2
0.6000000000000001
.4 + .1 + .1
0.6

在Julia 0.3中也是如此。

julia> .4 + .2
0.6000000000000001

julia> .4 + .1 + .1
0.6

和Scala:

scala> 0.4 + 0.2
res0: Double = 0.6000000000000001

scala> 0.4 + 0.1 + 0.1
res1: Double = 0.6

和Haskell:

Prelude> 0.4 + 0.2
0.6000000000000001    
Prelude> 0.4 + 0.1 + 0.1
0.6

但是R v3是正确的:

> .4 + .2
[1] 0.6
> .4 + .1 + .1
[1] 0.6

3 个答案:

答案 0 :(得分:45)

所有这些语言都使用系统提供的浮点格式,它表示二进制中的值,而不是十进制中的值。 0.20.4等值无法以该格式精确表示,因此会存储最接近的可表示值,从而导致较小的错误。例如,数字文字0.2会生成一个浮点数,其精确值为0.200000000000000011102230246251565404236316680908203125。类似地,对浮点数进行的任何给定算术运算都可能导致一个不能完全表示的值,因此真正的数学结果将替换为最接近的可表示值。这些是你所看到的错误的根本原因。

然而,这并没有解释语言之间的差异:在所有的例子中,正在进行完全相同的计算,并且得到了完全相同的结果。不同之处在于各种语言选择显示结果的方式。

严格来说,您显示的答案的 none 是正确的。使用舍入到最接近的舍入模式(相当安全)假设IEEE 754二进制64算术,第一个和的精确值是:

0.600000000000000088817841970012523233890533447265625

而第二笔的确切值是:

0.59999999999999997779553950749686919152736663818359375

但是,这些输出都不是特别方便用户使用,显然您测试的所有语言都是在打印时缩小输出的合理决定。但是,他们并没有采用相同的策略来格式化输出,这就是你看到差异的原因。

格式化有许多可能的策略,但有三个特别常见的策略是:

  1. 计算并显示17个正确舍入的有效数字,可能会在它们出现的地方删除尾随零。 17位数的输出保证了不同的binary64浮点数将具有不同的表示形式,因此可以从其表示中明确地恢复浮点值; 17是具有此属性的最小整数。例如,这是Python 2.6使用的策略。

  2. 计算并显示在通常的round-ties-to-even舍入模式下舍入到给定binary64值的最短十进制字符串。这比策略1实现起来要复杂得多,但保留了不同浮点数具有不同表示的属性,并且倾向于产生令人愉快的输出。这似乎是您测试的所有语言(除了R)正在使用的策略。

  3. 计算并显示15(或更少)正确舍入的有效数字。这具有隐藏十进制到二进制转换中涉及的错误的效果,给出了精确十进制算术的错觉。它的缺点是不同的浮点数可以具有相同的表示。这似乎是R正在做的事情。 (感谢@hadley在评论中指出有一个R setting控制用于显示的位数;默认是使用7位有效数字。)

答案 1 :(得分:6)

您应该知道0.6无法在IEEE浮点中准确表示,0.40.20.1也不能。这是因为比率1/5是二进制的无限重复分数,就像1/31/7之类的比例是十进制的。由于您的初始常量都不准确,因此您的结果也不准确也就不足为奇了。 (注意:如果你想更好地处理这种缺乏准确性的问题,请尝试从计算结果中减去你期望的值......)

还有许多其他潜在的陷阱。例如,浮点运算仅近似关联:在不同的顺序中将相同的数字组合在一起通常会给您略微不同的结果(偶尔会给您非常不同结果)。因此,在精度很重要的情况下,您应该注意如何累积浮点值。

这种情况的通常建议是"What Every Computer Scientist Should Know About Floating Point Arithmetic"David Goldberg。要点:浮点不准确,可能不支持关于其行为的天真假设。

答案 2 :(得分:4)

原因是因为它根据IEEE浮点运算标准在最后进行了四舍五入:

http://en.wikipedia.org/wiki/IEEE_754

根据标准:加法,乘法和除法应该完全正确到最后一位。这是因为计算机具有有限的空间来表示这些值,并且无法无限追踪精度。