Java并行求和与单线程求和存在精度差异的原因探究
并行计算数组倒数和的精度差异问题
我实现了继承自RecursiveAction的Sum类,用于计算数组元素的倒数之和(Σ1/a[i])。具体代码如下:
public class Sum extends RecursiveAction { int[] items; double result; int min = 100000; int from, to; Sum(int[] items, int from, int to) { this.items = items; this.from = from; this.to = to; } @Override protected void compute() { if (to - from <= min) { for (int i = from; i < to; i++) { result += 1d / items[i]; } } else { var mid = (from + to) / 2; var left = new Sum(items, from, mid); var right = new Sum(items, mid, to); invokeAll(left, right); result += left.result + right.result; } } }
运行后得到以下结果:
Single: 1.3180710500108106E8 Total time: 0.612 Parallel: 1.3180710501986596E8 Total time: 0.18
可以看到单线程与并行计算的结果非常接近但存在微小精度差异,而若去掉倒数运算直接求和数组元素,结果则完全一致。请问这一差异的成因是什么?
精度差异的成因
- 浮点数加法不满足结合律:这是问题的核心本质。单线程计算时,严格按照数组顺序依次累加每个倒数项;并行计算时,数组被拆分成多个子块独立累加,最后合并子块结果。由于二进制浮点数无法精确表示大多数小数,每个倒数项本身就是近似值,不同的累加顺序会让误差的累积路径不同,最终产生可观测的微小差异。
- 倒数运算放大了精度敏感度:直接对int数组求和时,int类型值可以被double精确存储(double的53位有效位完全覆盖int的32位取值范围),每个求和项都是精确值。这种情况下,累加顺序带来的误差累积极低,在你的测试场景中表现为结果完全一致。但计算倒数后,
1d / items[i]的结果几乎都是不精确的近似值,这些近似值在不同累加顺序下的误差叠加效应被放大,最终显现出差异。
内容的提问来源于stack exchange,提问作者GangYeah
相关产品推荐
相关产品推荐

