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

为何Numpy采用的成对求和比朴素求和的误差更小?

为什么成对求和比朴素求和的数值误差更小?

Numpy的sum函数采用成对求和而非朴素求和,以此获得更优的数值误差表现,下面就直观拆解成对求和降低误差的核心逻辑。

朴素求和

朴素求和是按顺序依次累加,计算形式为:

sum = (((x1+x2)+x3)+…+xn)

这种方式的平均误差为O(√N),最坏情况误差可达O(N)。问题出在:当累加过程中出现大数加小数时,小数的尾数会因浮点数的精度限制被截断,每次截断产生的误差会随着累加次数线性累积。

成对求和

成对求和通过递归两两配对相加来降低误差,步骤如下:

  • 初始配对:以原始数字列表为起点:{x1,x2,x3,x4,…,xn}
  • 成对求和:将列表中的数字两两求和:{(x1+x2),(x3+x4),…}
  • 递归求和:重复上述步骤,直到得到单个求和结果

这种方式的平均误差为O(log√N),最坏情况误差为O(logN),误差量级远低于朴素求和。

核心改进点:减少误差累积的次数与量级

浮点数相加的误差根源是尾数对齐时的截断:当两个数的量级差距超过浮点数的尾数位数时,小数会被完全“吃掉”(比如3位尾数下,1e10 + 1e-10的结果还是1e10)。

朴素求和的问题

在朴素求和中,如果先加一个大数,后续所有小数都会被这个大数“掩盖”,每加一个小数就产生一次截断误差,误差累积次数是线性的(O(N))。比如用3位尾数计算1e10 + 1e-10 + 1e-10 + ... + 1e-10(1000个小数),每次加小数都被截断,最终结果还是1e10,总误差是1e-7,完全丢失了所有小数的贡献。

成对求和的优势

成对求和优先让量级相近的数先相加:

  1. 先把所有小数两两相加,逐步得到更大的数(比如1000个1e-10先加成500个2e-10,再成250个4e-10……直到得到1e-7)
  2. 这时再和1e10相加,虽然还是有截断,但至少保留了小数的总贡献,误差只产生一次,而非1000次

另外,递归配对的方式让误差的累积次数变成对数级:N个数需要log₂N轮配对,每轮的误差只会在当前层级累积,不会像朴素求和那样线性叠加。

补充例子(更能体现差异)

用3位尾数计算:1e10 + 1e-10 * 1000

  • 朴素求和:(((1e10 + 1e-10) + 1e-10) + ...) = 1e10,总误差1e-7
  • 成对求和:先把1000个1e-10配对累加得到1e-7,再和1e10相加,结果为1.0000000000001e10(3位尾数下显示为1.00e10,但实际保留了更多有效信息),误差远小于朴素求和。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 16:47:04