假设我们在c++中有一个小数组(约10^(-15)
)个数组。如果我们按顺序计算此数组中的数字总和,例如
double sum = 0;
for (int i = 0; i < n; i++) sum+=array[i];
我们获得了一些价值x
。
但是如果我们将一个数组分成几个部分,然后计算每个部分的总和,然后我们将所有部分和加在一起,我们得到一些值x2
,它接近x
但是不完全是x
。所以我在计算总和方面已经失去了成功。
有人知道如何通过将这些数字划分为某些部分来计算小双数的总和而不会失去准确性吗?
答案 0 :(得分:16)
#include <numeric>
#include <iostream>
#include <vector>
struct KahanAccumulation
{
double sum;
double correction;
};
KahanAccumulation KahanSum(KahanAccumulation accumulation, double value)
{
KahanAccumulation result;
double y = value - accumulation.correction;
double t = accumulation.sum + y;
result.correction = (t - accumulation.sum) - y;
result.sum = t;
return result;
}
int main()
{
std::vector<double> numbers = {0.01, 0.001, 0.0001, 0.000001, 0.00000000001};
KahanAccumulation init = {0};
KahanAccumulation result =
std::accumulate(numbers.begin(), numbers.end(), init, KahanSum);
std::cout << "Kahan Sum: " << result.sum << std::endl;
return 0;
}
输出:
Kahan Sum: 0.011101
代码here。
答案 1 :(得分:4)
数字的绝对大小不是问题。
如果您想要更准确的求和,您是否考虑过补偿金额? http://en.wikipedia.org/wiki/Kahan_summation_algorithm
但是,如果您的意思是在不失去任何准确度的情况下,您的结果将不一定适合双倍。如果这真的是你想要的,你可以看看http://dl.acm.org/citation.cfm?id=1824815或类似的算法908。
答案 2 :(得分:2)
在这些情况下的诀窍是首先将数组从小到高排序,然后在你所做的循环中求和。这样,准确性最好。
答案 3 :(得分:2)
考虑对整套或每个子集应用Kahan summation algorithm。
还有其他questions引用此算法可以帮助您
答案 4 :(得分:1)
计算机中的双数字存储在二进制数字系统中。这就是为什么当你看到一个double值(十进制表示法)时,你实际上看到了带有一些舍入的double值(例如0.1是无限分数)。您可以进行相同的实验,其中double值为2度(例如2 ^( - 30)),然后您将看到值将匹配。
在对不同顺序的双精度值求和时观察到差异的原因是,在每次计算后,结果将在二进制数值系统中舍入,因此与实际值略有差异。
答案 5 :(得分:1)
用于表示十进制数的二进制浮点数比精度更精确。你找到了一种表达差异的方法。
答案 6 :(得分:1)
它可以是你的个别求和被优化并以80位寄存器执行,但随后转回64个双倍(读取编译器开关)。当然这会失去精确度。如果是这种情况,那么分解数组并添加单独的64位总和将给出一个不同的答案,将它们全部添加为80位a并将总计转换回来。
这可能不是原因,但可能值得进一步研究。查看this question
的所选答案答案 7 :(得分:0)
在处理正常大小的数字处理非常小的数字时,添加数字的结果的精度损失没有差别。可能相关的是: a)数字之间的大小相对差异大吗? b)数字不同的SIGNS?
最后一个问题通常与加精度有关。 你应该做什么 - 可能不是完全最优的,但是公平的,并且易于实施 - 是:
a)将它们分别分为正数和负数的子集
b)对每个子集进行排序
然后
c)取两个组合中最大的(绝对大小),并用该数字初始化你的总和,并将其从列表中删除
d)迭代地:每当当前总和为正数时,取最大剩余负数并将其加到总和中,并将其从列表中删除;每当目前的金额为负时,也要这样做。
通过这种方式,你很有可能(几乎)将精确度的损失降到最不可避免的范围内(给出数字的表示)。