2. 数值精度与舍入误差异常
在计算物理模型中,绝大多数“结果看起来不对”的异常并非来自物理模型本身,而是来自数值表示与运算的有限精度。IEEE 754 双精度浮点数(float64)只有约 15~17 位有效十进制数字,尾数 52 位,机器精度 ε≈2.22×10⁻¹⁶。任何一次加减乘除都会引入不超过 0.5 ulp 的舍入,而模型往往要经历成千上万次运算,误差因此可能被放大、累积,甚至被某一步骤“引爆”。本节围绕三类典型异常——浮点表示误差、累积误差、灾难性抵消——说明如何识别它们,并给出可落地的缓解手段。
一、三类误差的来源与判别特征
浮点表示误差来自十进制小数无法用二进制有限表示。例如 0.1 在双精度下的实际值是 0.1000000000000000055511151231257827…,因此 0.1 + 0.2 == 0.3 在多数语言中为假。这类误差量级在 1e-17 左右,单独看无害,但若用于相等判断、循环终止条件或索引计算,就会表现为“明明该相等却不相等”“循环多跑一次或少跑一次”。
累积误差出现在大量重复运算中,典型场景是长时间积分、迭代求解和求和。以显式欧拉法积分谐振子为例,每步局部截断误差为 O(Δt²),但舍入误差每步约 ε·|x|,N 步后随机游走式累积约 √N·ε·|x|,最坏情况线性累积 N·ε·|x|。当 N=10⁷、|x|≈1 时,仅舍入就可能带来 1e-9~1e-12 量级的漂移;若模型本身对能量守恒敏感,这种漂移会表现为“能量缓慢单调上升”。
灾难性抵消是最危险的一类:两个相近的大数相减,有效数字被大量抵消。例如计算 f(x)=√(x+1)−√x 在 x=1e8 时,两个根号值都约为 1e4,直接相减只剩约 5e-5,而输入本身的舍入误差约 1e-12,相对误差被放大到 1e-7 量级;若 x 更大,结果可能完全失真。判别特征是:结果量级远小于参与运算的操作数量级,且对输入微小扰动极其敏感。
二、识别:从现象反推误差类型
排障时不要一上来就改代码,先做“精度对照实验”:把同一模型分别用 float32、float64 和(必要时)高精度库运行,比较关键输出。若 float32 与 float64 结果差异显著、且 float64 与高精度结果接近,则异常基本可判定为精度问题;若三者都不收敛到同一值,则更可能是模型或离散化错误。这个对照实验是区分“数值异常”与“物理异常”的最快手段。
针对累积误差,可在积分循环中每隔固定步数记录守恒量,例如谐振子的总能量 E=½mv²+½kx²。若 E 呈单调缓慢上升且斜率随步数近似恒定,多为系统性舍入偏置;若呈随机游走式抖动,多为随机舍入累积。针对灾难性抵消,可对输入施加 1e-12 量级的相对扰动,观察输出相对变化是否被放大 10³ 倍以上——若是,则几乎可以确定存在抵消。
三、缓解:可执行的实现细节
补偿求和是抑制累积误差的通用手段。Kahan 求和的核心是把每次舍入丢失的低位保存在补偿变量 c 中:
def kahan_sum(xs):
s = 0.0
c = 0.0
for x in xs:
y = x - c
t = s + y
c = (t - s) - y
s = t
return s
对 10⁷ 个量级差异较大的数求和,朴素求和可能损失 4~6 位有效数字,而 Kahan 求和可把误差压到接近 1 ulp。若数据可整体获得,更简单的是 numpy.sum 的成对求和(pairwise summation),它已内置较好的误差控制。
代数变形是消除灾难性抵消最优雅的方式。对 √(x+1)−√x,分子有理化得 1/(√(x+1)+√x),在 x=1e8 时直接计算约为 5.0e-5,且不再有大数相减。类似地,exp(x)−1 在 x 很小时应改用 expm1(x),log(1+x) 应改用 log1p(x),标准库已针对小量做了展开实现。
算法层面,长时间积分应优先选择辛积分器(如 velocity-Verlet),它不保证每步能量精确守恒,但能保证能量在有限范围内有界振荡而不长期漂移,这比减小步长更有效。对递推关系 x_{n+1}=f(x_n),若发现误差随 n 线性增长,可每隔 M 步用一次高精度重算“重置”状态,把累积误差截断在 M·ε 量级。
四、一个完整的排障示例
假设某同学用显式欧拉法模拟弹簧振子,k=1、m=1、x₀=1、v₀=0、Δt=0.01,运行 10⁶ 步后发现总能量从 0.5 缓慢升到约 0.62。排查顺序应为:先换 velocity-Verlet,若能量变为有界振荡(例如在 0.5±0.002 内),说明原异常主要来自积分器的系统性误差而非舍入;再对同一 Verlet 版本分别用 float32 与 float64 运行,若两者能量包络差异小于 1e-6,则舍入累积可忽略。若仍怀疑求和误差,可把能量计算改为 Kahan 求和再对比。通过“换算法—换精度—换求和方式”三级对照,即可把异常定位到具体环节,而不是笼统地“调小步长”。
需要强调的是,缓解精度异常的目标不是追求“零误差”,而是让误差量级低于模型可接受的物理容差,并且不随运行时间失控增长。识别靠对照实验,缓解靠补偿算法、代数变形与保结构积分器,验证则回到守恒量与精度收敛性上——这条闭环适用于本节之后绝大多数数值类异常案例。