You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中Stream的mapToDouble.sum()结果随输入顺序不同出现误差的原因

public static void main(String[] args) {
    double sum=860.10+1808.09;
    double sum1=1808.09+860.10;
    
    System.out.println(sum);// 输出 2668.19
    System.out.println(sum1);// 输出 2668.19

    List<Double> lst=new ArrayList<Double>();
    lst.add(860.10);
    lst.add(1808.09);
    Double res=lst.stream().mapToDouble(d->d).sum();
    System.out.println(res);// 输出 2668.1900000000005
    
    lst.clear();
    // 倒序添加元素
    lst.add(1808.09);
    lst.add(860.10);
    
    res=lst.stream().mapToDouble(d->d).sum();
    System.out.println(res);// 输出 2668.19
}
问题解答

1. 浮点数本身的精度限制

double是基于IEEE 754标准的二进制浮点数,无法精确表示所有十进制小数。你代码中的860.10、1808.09转换为二进制存储时本身就存在微小的舍入误差,并非完全等于你写的十进制字面量值,这是所有误差出现的基础前提。

2. 双数直接相加顺序不影响的原因

两个double值直接相加时,仅执行一次加法运算,二进制浮点数的单次加法满足交换律,交换两个操作数的位置不会改变运算结果,因此你直接计算的两个sum值完全一致。

3. stream求和顺序影响结果的原因

Java的DoubleStream.sum() 为了降低多元素累加时的累计误差,没有采用简单的两两累加逻辑,而是实现了带误差补偿的Kahan求和算法:算法会额外维护一个补偿变量,用来抵消每次累加产生的微小误差,这个算法的计算过程高度依赖元素的累加顺序。
你这个测试场景下:

  • 先加较小的860.10、再加较大的1808.09时,补偿项的计算结果刚好保留了底层的舍入误差,最终输出就出现了2668.1900000000005的偏差
  • 反过来先加较大的1808.09、再加较小的860.10时,补偿项刚好抵消了底层的舍入误差,最终输出就和直接计算的结果一致

4. 解决方案

如果业务要求精确的十进制运算,不要使用float、double这类二进制浮点数,改为使用BigDecimal类型存储和计算数值即可完全避免这类精度问题。


内容的提问来源于stack exchange,提问作者Chenna

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 14:54:03