Answers:
添加任意浮点数通常会产生一些舍入误差,并且舍入误差将与结果的大小成比例。如果您计算一个总和,并首先从最大的数字开始相加,则平均结果将更大。因此,您将以最小的数字开始加法。
但是,如果产生四个和,您将获得更好的结果(并且运行速度更快),例如:以sum1,sum2,sum3,sum4开头并将四个数组元素依次加到sum1,sum2,sum3,sum4。由于每个结果平均仅是原始总和的1/4,因此您的错误小四倍。
更妙的是:成对添加数字。然后成对添加结果。再次成对添加这些结果,依此类推,直到剩下两个数字相加。
非常简单:使用更高的精度。使用long double来计算double的总和。使用double计算浮点数总和。
接近完美:查找前面描述的Kahan算法。最好以最小的数字开头添加。
这些是整数还是浮点数?假设它是浮点数,我会选择第一个选项。最好将较小的数字彼此相加,然后再添加较大的数字。使用第二个选项,随着我的增加,您最终将一个小数字增加到一个大数字,这可能会导致问题。这是有关浮点算法的一个很好的资源:每个计算机科学家都应该了解的浮点算法
animal_magic的回答是正确的,您应该将数字从最小到最大相加,但是我想举一个例子来说明原因。
假设我们使用的浮点格式可以使我们达到惊人的3位数精度。现在我们要添加十个数字:
[1000, 1, 1, 1, 1, 1, 1, 1, 1, 1]
当然,确切的答案是1009,但我们无法以3位数字的格式得到它。四舍五入到三位数字,我们得到的最准确答案是1010。如果我们将最小和最大相加,则在每个循环上我们将得到:
Loop Index s
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 1009 -> 1010
这样我们就可以为我们的格式获得最准确的答案。现在假设我们从最大到最小相加。
Loop Index s
1 1000
2 1001 -> 1000
3 1001 -> 1000
4 1001 -> 1000
5 1001 -> 1000
6 1001 -> 1000
7 1001 -> 1000
8 1001 -> 1000
9 1001 -> 1000
10 1001 -> 1000
由于在每次操作后都会对浮点数进行四舍五入,因此所有加法都将四舍五入,从而使我们的误差从精确值从1增加到9。现在,假设您要添加的数字集是1000,然后是100 1,还是一百万。请注意,要真正准确,您需要将最小的两个数字相加,然后将结果求和到数字集中。
对于一般情况,我将使用补偿求和(或Kahan求和)。除非数字已经排序,否则对它们进行排序将比加它们昂贵得多。补偿求和也比排序求和或天真的求和更为准确(请参阅上一个链接)。
至于参考资料,每个程序员都应该了解的浮点算术知识涵盖了足够多的基本知识,因此有人可以在20(+/- 10)分钟内阅读并理解基本知识。戈德伯格的“每位计算机科学家都应该知道的有关浮点算术的知识”是经典的参考书,但是我认识的大多数人都建议读者不要自己详细阅读该论文,因为该论文大约有50页(在某些情况下,甚至更多)印刷品),并以密集的散文形式撰写,因此我很难将其推荐给人们作为一线参考。再次查看该主题是有益的。百科全书是Higham的数值算法的准确性和稳定性,涵盖了该材料,以及许多其他算法中的数值误差累积;它也有680页,所以我也不会先看这个参考资料。
前面的答案已经讨论了这个问题,并给出了合理的建议,但是我想提一个额外的怪癖。在大多数现代体系结构上,for
无论如何,您描述的循环都将以80位扩展精度执行,这可以确保更高的准确性,因为所有临时变量都将放入寄存器中。因此,您已经有了某种形式的保护措施,以防止数字错误。但是,在更复杂的循环中,中间值将在两次操作之间存储在内存中,因此会被截断为64位。我猜可能是
s=0;
for \ i=1:n
printf("Hello World");
s=s + z_{i} ;
end
足以使求和的精度降低(!!)。因此,如果要在检查准确性的同时对代码进行printf调试,请务必小心。
对于感兴趣的人,本文描述了一个广泛使用的数值例程(Lapack的秩显示QR因式分解)中的问题,正是由于这个问题,其调试和分析非常棘手。
在这两个选项中,从较小到较大相加将产生较小的数值误差,然后从较大到较小相加。
但是,> 20年前,在我的“数值方法”课程中,讲师指出了这一点,但我发现,由于累加器和要添加的值之间的相对差异,这仍然带来了不必要的错误。
从逻辑上讲,一个更好的解决方案是在列表中添加2个最小的数字,然后将求和的值重新插入排序后的列表中。
为了证明这一点,我制定了一种算法,该算法可以通过使用从主数组中删除元素时释放的空间来构建总值的辅助数组,从而有效地(在空间和时间上)执行此操作,这些求和值自添加以来就固有地排序是总在增加的价值之和。在每次迭代中,然后检查两个数组的“技巧”以找到2个最小值。
使用二叉树加法,即选择分布的平均值(最接近的数字)作为二叉树的根,并通过在图的左侧添加较小的值,在右侧添加较大的值来创建排序的二叉树,依此类推。 。以自下而上的方式递归添加单个父级的所有子节点。这是有效的,因为平均误差会随着求和次数的增加而增加,并且在二叉树方法中,求和次数的数量级以2为底的log n顺序。因此,平均误差会更小。
我只在这里/programming//a/58006104/860099(当您转到此处时,单击以显示代码段并按按钮运行它)
这是一个JavaScript示例,清楚地表明,从最大开始的和给出了更大的误差
arr=[9,.6,.1,.1,.1,.1];
sum = arr.reduce((a,c)=>a+c,0); // = 9.999999999999998
sortSum = [...arr].sort().reduce((a,c)=>a+c,0); // = 10
console.log('sum: ',sum);
console.log('sortSum:',sortSum);