在总结大阵列时错误的结果

时间:2017-04-08 23:04:32

标签: c++


我总是得到错误的结果,而我试图总结大数组。我有以下代码示例的孤立问题(它不是一个大数组的总和,但我相信这是问题)

可编辑样本:

template <typename T>
void cpu_sum(const unsigned int size, T & od_value) {
    od_value = 0;
    for (unsigned int i = 0; i < size; i++) {
        od_value += 1;
    }
}

int main() {
    typedef float Data;
    const unsigned int size = 800000000;
    Data sum;
    cpu_sum(size, sum);

    cout << setprecision(35) << sum << endl; // prints: 16777216 // ERROR !!!
    getchar();
}

环境:

操作系统:Windows 8.1 x64主页
IDE: Microsoft Visual Studio 2015

错误说明:

虽然我的结果显然应该是sum == 800000000,但我仍然会sum == 16777216
这对我来说非常奇怪,因为浮动最大值远远高于这个,但看起来sum变量达到了极限。

我错过了什么?

2 个答案:

答案 0 :(得分:4)

这是一个众所周知的问题。渐渐地,你的总和变得如此之大,以至于下一个求和变得与它的epsilon(大约10 ^ -14)相当。那一刻你开始失去精确度。

标准解决方案是改变求和策略:当数组大于100个元素时,将它分成两半并分别对每一半求和。它以递归方式进行,并且倾向于更好地保持精度。

答案 1 :(得分:2)

正如Michael Simbursky所指出的,当尝试对大量有限精度数进行求和时,这是一个众所周知的问题。他提供的解决方案效果很好,效率很高。

对于好奇的读者来说,一个较慢的方法是在计算总和之前对数组进行排序,确保以增加绝对值的顺序添加值。这可以确保最不重要的值在被其他值淹没之前对总和进行贡献。与任何严肃的编程企业相比,这种技术更多地用作示例/说明。

对于严重的科学编程,其中数据的整体大小可能有很大差异,另一个需要注意的算法是Kahan Summation Algorithm,引用的维基百科页面提供了很好的描述。