假设我们有小的阵列(大约10^(-15)
双号中的C ++ 。 如果我们计算数字的总和在该阵列顺序,例如
double sum = 0;
for (int i = 0; i < n; i++) sum+=array[i];
我们得到了一些价值x
。
但是,如果我们把一个数组到一些地方,然后计算总和中的每个部分,在这之后,我们添加所有部分和在一起,我们得到了一些值x2
,这是接近x
,但不完全是x
。 所以我在计算总和失去accruacy。
是否有人知道如何划分这些数字到一些地方没有松动的精度计算小双数的总和?
使用Kahan的总和 :
#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
代码在这里 。
这些数字的绝对大小不是问题。
如果你想有一个更准确的总和,你有没有考虑补偿金额? http://en.wikipedia.org/wiki/Kahan_summation_algorithm
但是,如果你真的是不丢失任何的准确性,你的结果并不一定适合在双。 如果这是真的,你想要什么,你可以看看算法908 http://dl.acm.org/citation.cfm?id=1824815或相似。
在这些情况下,关键是要一阶从小到更高的数组,然后在你所做的循环和再。 这样,精度最好。
您还可以检查Kahan的总和算法
考虑到申请Kahan的求和算法为您的整个集合或每个子集。
还有其他的问题,引用该算法可以帮助你
在计算机中的双数字存储在二进制数字系统。 这就是为什么当你其实看到双值(十进制),你看到了一些舍入的double值(例如0.1是无限的分数)。 你可以做同样的实验,其中双值是2度(例如2 ^( - 30)),然后你会看到这些值将匹配。
您在总结在不同的序列中的双重价值观察差异的原因是,每个计算后的结果将在二进制数字系统被四舍五入,因此与实际值相差不大会出现。
用于表示十进制数的二进制浮点数字比精度更精确。 你已经找到堆焊差异的一种方式。
这可能是因为你的个人求和被优化,并且在80位寄存器中进行,但然后转移回到64个双打(读有关编译器开关)。 当然,这会失去精度。 如果是这种情况,那么分手的数组和添加单个64位到的款项会给出不同的答案,将他们全部为80位的A和总计转换回来。
这可能不是原因,但它可能是值得进一步研究。 看看所选择的回答这个问题
从处理正常大小的数字非常小的数字打交道时的精确度在增加数量的结果损失不不同。 什么可能是相关的是:1)在大小号之间的相对差异大? b)的数量不同的符号?
最后一个问题通常在与除了精密股份。 你应该做的 - 也许不是完全最优的,但一个公平的机会,并容易实现 - 是:
a)以积极和消极的子集分别将它们分割
B)进行排序每个子集
然后
三)采取从两组最大(以绝对尺寸)相结合,并与数初始化总和,并从列表中移除
d)迭代:每当当前总和为正,取最大的剩余的负并将其添加到总和,从列表中删除; 只要当前总和是负的,也这样做。
通过这种方式,你有你(almost-)最小的精度损失什么本质上是不可避免的(给定数字的呈现)一个公平的机会。