Python中math.fsum与循环求和结果差32.0的原因探究
为什么Python中math.fsum和循环累加浮点数的结果差32.0?
首先看测试代码:
import math nums = [26015151255025000.,26015151255025000.,26015151255025000.,26015151255025000.,26015151255025000.,26015151255025000.,26015151255025000.,26015151255025000.,26015151255025000.] math_fsum_result = math.fsum(nums) loop_result = 0.0 for n in nums: loop_result += n print(format(math_fsum_result, '.1f')) print(format(loop_result, '.1f'))
运行输出:
234136361295224992.0
234136361295224960.0
已知用decimal计算的正确值是234136361295225000.0,两种求和方法的结果刚好差32.0,核心原因在于浮点数的精度限制和两种算法的误差处理逻辑差异:
1. 单个浮点数的存储误差
Python的float是64位双精度浮点数,遵循IEEE 754标准,它的尾数部分最多能精确表示53位二进制整数。你的原始数值26015151255025000转换成二进制后,有效位数超过了53位,所以存储为float时会被舍入成26015151255024992.0——比原数小8,这是初始误差来源。
2. 普通循环累加的误差累积
普通累加是逐次把当前结果和下一个数相加,每次相加如果结果无法用双精度精确表示,就会截断低位,丢失的精度会累积:
- 前几次累加的结果还能被精确表示,但加到第7次之后,当前结果的二进制位数已经很高,再加上下一个数时,较小数的低位会被截断,每次这样的累加都会额外丢失16的精度。
- 9个数值累加下来,普通累加的总误差是-40(比正确值小40)。
3. math.fsum的误差补偿机制
math.fsum采用了补偿求和算法(类似Kahan求和的优化版本),它会在累加过程中额外跟踪那些被截断的微小误差值,后续累加时会把这些丢失的部分重新加回去,最大程度减少误差累积。
- 在这个例子里,fsum把单个数值的存储误差(每个-8,共-72)大部分补回来了,最终只比正确值小8,总误差是-8。
差值为32.0的原因
普通累加的总误差是-40,fsum的总误差是-8,两者的误差差就是(-8) - (-40) = 32,所以两个结果刚好相差32.0。这不是巧合,是双精度浮点数的位数限制和两种算法的误差处理逻辑共同导致的。
内容的提问来源于stack exchange,提问作者Firestrand
相关产品推荐
相关产品推荐

