第 1–2 章 · 数值品味的底线
这本是一本"配方手册",但第 1-2 章讲的是配方背后的品味:什么样的数值错误会毁掉计算,什么样的习惯能提前发现它们。这两章短,却是全书质量的地基。
误差的三种来源与对应的坏味道
- 舍入误差:浮点只有约 16 位十进制精度,大量相减相近数(灾难性抵消)会把有效位吃光。坏味道:公式里出现 $a-b$ 且 $a\approx b$——改写公式(如用 $x-y = \frac{x^2-y^2}{x+y}$ 类恒等变换)。
- 截断误差:用有限阶近似替代无限过程(如用短级数代替无穷级数)。坏味道:加密网格/加项后结果仍在漂移。
- 病态放大:问题本身对输入敏感。坏味道:微小参数扰动引起巨大输出变化——这时要改问题(正则化、重新参数化),不是改算法。
浮点的必修常识
- 判断相等永远用 $\|x-y\| \le \epsilon\max(\|x\|,\|y\|)$,不用 "=="。
- 求和精度:小数在前、成对求和(pairwise)或 Kahan 补偿求和;$10^8$ 个数朴素求和能丢掉一半有效位。
- 下溢、上溢的处理:中间量用对数域或缩放(如贝塞尔函数、阶乘的计算)。
"好程序"的数值清单
书中列的规矩至今有效:参数检查(负数开方、零除)、迭代法必配最大步数与收敛容差、避免隐式假设(假设矩阵正定、假设函数凸)。所有配方函数都带错误退出路径——这是它区别于课堂代码的地方。
老师的提醒
- 把"收敛了吗"打印出来成为习惯:任何迭代配方不报告残差就调用,等于盲飞。
- 灾难性抵消的快速体检:看结果对输入第 8 位有效数字的敏感度,反应剧烈就要重写公式。
自检
- 三种误差各自的典型症状与对策?
- Kahan 求和解决什么问题?代价是什么?
- 为什么说"问题的病态要改问题、算法的不稳要改算法"?
更深一层:数值品味的本质是"误差预算"思维
第 1-2 章教的其实是一种会计学:任何计算都有一个误差预算,工程师的职责是知道预算花在哪了。一次典型的科学计算有四本账:模型误差(物理假设的近似)、离散误差(网格/步长截断)、舍入误差(浮点表示)、统计误差(如果用了随机方法)。新手把所有误差混为一谈,熟手先问"哪一本账最大"——因为优化小账是浪费:离散误差 $10^{-3}$ 主导时,把舍入从 $10^{-15}$ 压到 $10^{-16}$ 毫无意义。这个"木桶效应"思维把数值方法的无限细节压缩成一个可执行的流程:每做一个计算,先估四本账的量级,再决定优化谁。
舍入误差还有个隐蔽的放大器值得单独认识:条件放大。$fl(a+b)$ 本身只错 $\mathbf{u}$ 量级,但如果 $a + b$ 之后还要做 $(a+b) - c$ 且 $a + b \approx c$(灾难性抵消),结果的相对误差就被放大成 $\mathbf{u} \cdot |a+b|/|结果|$——中间量很大、结果很小时,16 位精度瞬间蒸发。识别这类结构的直觉("两个大数相减求小量")是数值品味的核心,解药是恒等变形(如二次方程求根用 $q = -\frac12(b + \mathrm{sign}(b)\sqrt{b^2-4ac})$ 的稳定形式)。
推导细读:Kahan 补偿求和为什么能"作弊"
朴素求和的误差 $\le (n-1)\mathbf{u}S$ 来自每步把小加数"丢进"大和里时的舍入。Kahan 的算法用一个补偿变量 $c$ 把每步被丢掉的低位记账:
其中 $c = (t - S) - y$ 这一步在数学上恒为零,但浮点下它恰好等于"这一步丢失的量"——下一步用 $x_i - c$ 把它找补回来。误差从 $O(n\mathbf{u}S)$ 降到 $O(\mathbf{u}S)$,与 $n$ 无关。代价:每步 4 次浮点操作(约 4 倍耗时)。工程变体:成对求和(误差 $O(\mathbf{u}S\log n)$、可向量化)、numpy 的 pairwise 默认、并行归约的树形结构——同一个问题的四种"账本管理策略"。
在真实世界里:一次真实的事故复盘
数值品味的经典教学案例:某弹道仿真中,导弹落点误差随飞行时间发散,团队排查传感与模型无果,最终定位到状态更新里两个相近大数相减(位置更新用了 $x_{new} = x + v\Delta t$,而后续修正项基于 $x_{new}$ 与参考弹道之差)——长飞行时间下参考弹道与实际弹道都巨大、差值微小,16 位精度被吃光。修复:改用偏差量积分(积分相对参考弹道的差分而非绝对位置)。教训的通用形式:长时间积分系统,状态变量应选"偏离参考的小量"而非绝对量——航天器轨道力学、量子化学的 SCF、气候模式的通量守恒约束,全是这一条的应用。
常见误区与辨析
- "double 就够了":对条件数温和的问题够;条件放大或超长求和链会让 16 位不够——误差预算思维帮你判断"够不够",而不是教条。
- "收敛了就对了":迭代法收敛到的是离散方程的解;离散误差独立存在,两条账不能互相抵赖。
- "数值错误总会显现为崩溃":多数数值错误是静默的——不崩溃、结果看着合理、偏差藏在小数点后;只有误差预算习惯能暴露它们。
专题深入:误差预算的"四本账"实务
误差预算思维值得做成可执行的清单:模型误差(物理假设的近似——湍流模型、材料模型)、离散误差(网格/步长截断——可估可控)、舍入误差(浮点表示——$\mathbf{u}$ 级)、统计误差(随机方法——$N^{-1/2}$ 级)。每做一个计算,先估四本账的量级,再决定优化谁——木桶效应:优化小账是浪费(离散误差 $10^{-3}$ 主导时压舍入无意义)。这个思维在工程报告里的形态:误差来源的分解表——负责任的数值报告应该能说出每一层的量级。
灾难性抵消的识别直觉("两个大数相减求小量")是数值品味的核心:$x - y$ 且 $x \approx y$ 时相对误差被放大 $\frac{|x|+|y|}{|x-y|}$ 倍。解药是恒等变形(二次方程求根的稳定形式、双曲函数的差分形式)——看到相近数相减就改写公式。
数学细节展开:Kahan 求和的"记账"机制
Kahan 补偿求和的机制值得展开:补偿变量 $c$ 把每步被丢掉的低位记账——$c = (t - S) - y$ 这一步在数学上恒为零,但浮点下它恰好等于"这一步丢失的量",下一步用 $x_i - c$ 找补回来。误差从 $O(n\mathbf{u}S)$ 降到 $O(\mathbf{u}S)$——与 $n$ 无关。代价:每步 4 次浮点操作(约 4 倍耗时)。成对求和(误差 $O(\mathbf{u}S\log n)$、可向量化)与 Kahan(误差与 $n$ 无关、不可向量化)的权衡是现代并行求和的设计空间。
工程案例细节:数值品味的"代码清单"
NR 前两章的规矩值得做成代码审查清单:参数检查(负数开方、零除、数组越界)、迭代法必配最大步数与收敛容差(不收敛要有明确报错而非静默)、避免隐式假设(假设矩阵正定、假设函数凸——假设要在文档里写清)、所有配方函数都带错误退出路径。"好程序"的数值清单是"坏结果"的防火墙——每一条都对应一类真实事故。
常见误区与辨析(补充)
- "double 就够了":对条件数温和的问题够;条件放大或超长求和链会让 16 位不够——误差预算思维帮你判断"够不够"。
- "收敛了就对了":迭代法收敛到的是离散方程的解;离散误差独立存在,两条账不能互相抵赖。
例题
计算 $\sum_{i=1}^{10^8} 1$(双精度)。朴素顺序求和的最坏误差是多少?成对求和呢?
解答
朴素求和:部分和最终为 $10^8$,每步舍入 $\le \mathbf{u}\cdot S_i$,最坏总误差
——错到个位数(相对误差 $10^{-8}$,约 8 位有效数字没了 8 位中的一大半误差积累在尾部大和数上)。
成对求和:误差 $\approx \log_2 n\cdot\mathbf{u}\cdot S = 27\times1.1\times10^{-16}\times10^8 \approx 3\times10^{-7}$——好 6 个数量级。
Kahan 补偿:同样 $\sim O(\mathbf{u}S)$ 与 $n$ 无关。工程结论:大数组求和永远不要朴素顺序累加。