为什么从最大到最小的浮点数添加不如从最小到最大添加?

时间:2018-01-06 05:50:17

标签: java floating-point rounding-error

我的Java教科书指出,在处理浮点数时,从最大到最小的添加不如从最小到最大添加准确。但是,他并没有明确解释为什么会这样。

2 个答案:

答案 0 :(得分:2)

浮点数的精度位数有限(float为6,double为15)。计算

1.0e20d + 1 

给出结果1.0e20,因为没有足够的精度来表示数字

100,000,000,000,000,000,001

如果您从最大数字开始,那么任何超过n个数量级的数字(n为6或15,取决于类型)将不会对总和做出贡献。从最小的开始,您可以将几个较小的数字合并为一个会影响最终总数的数字。

它会产生影响的地方,例如

1.0e20 + 1.0e4 + 6.0e4 + 3.0e4

假设它精确到15位十进制数字(不是,请参阅下面的链接文章,但15对于该示例来说已经足够了),如果从较大的数字开始,其他任何一个都不会有所作为,因为它们是太小。如果从较小的那些开始,它们总计为1.0e5,这足以影响最终总数。

请阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 1 :(得分:0)

Nick Higham在section 4.2 of "Accuracy and Stability of Numerical Algorithms"中提供了一个很好的解释。以下是我对此的随意解释:

浮点的关键属性是,当单个操作的结果无法准确表示时,它会四舍五入到最接近的值。这有很多后果,即加法(和乘法)不再是associative

需要注意的另一个主要问题是错误(真值和舍入值之间的差异)是相对的。如果我们使用方括号([])来表示此舍入操作,那么我们将拥有任何数字x的属性:

|[x] - x| <= ϵ |[x]| / 2

其中ε是machine epsilon

假设我们要总结[x1, x2, x3, x4]。显而易见的方法是通过

s2 = x1 + x2
s3 = s2 + x3 = x1 + x2 + x3
s4 = s3 + x4 = x1 + x2 + x3 + x4

如上所述,我们不能完全这样做,所以我们实际上在做:

t2 = [x1 + x2]
t3 = [t2 + x3] = [[x1 + x2] + x3]
t4 = [t3 + x4] = [[[x1 + x2] + x3] +x4]

结果错误|t4 - s4|有多大?我们知道

|t2 - s2| = |[x1+x2] - (x1+x2)| <= ϵ/2 |t2|

现在我们可以写Triangle inequality

|t3 - s3| =  |[t2+x3] - (t2+x3) + (t2+x3) - (s2+x3)| 
          <= |[t2+x3] - (t2+x3)| + |t2 - s2|
          <= ϵ/2 (|t3| + |t2|)

再次:

|t4 - s4| =  |[t3+x4] - (t3+x4) + (t3+x4) - (s3+x4)| 
          <= |[t3+x4] - (t3+x4)| + |t3 - s3|
          <= ϵ/2 (|t4| + |t3| + |t2|)

这导致了Higham的一般建议:

  

在设计或选择求和方法以实现高精度时,目标应该是最小化中间和ti的绝对值。

因此,如果您正在进行顺序求和(就像我们上面所做的那样),那么您希望从最小的元素开始,因为这将为您提供最小的中间和。

但这不是唯一的选择!还有pairwise summation,您可以在树形式中添加对(例如[[x1 + x2] + [x3 + x4]]),但这需要分配工作数组。您还可以通过将中间和存储在向量中来利用SIMD vectorisation,这可以提高速度和精度。