机器精度 · 误差有多大,以及怎么压住它
「存不准」到底不准多少?机器精度 ε 给出了精确标尺——单次运算的相对误差不超过它。这一章量化 float64 的精度(约 16 位有效数字),看误差在长累加里怎么滚大,并用 Kahan 补偿求和把它压住。
机器精度 · 误差有多大,以及怎么压住它
上一章说浮点数「存不准」。不准多少?这不是玄学,有个精确的标尺——机器精度(machine epsilon,记 )。
机器精度:相邻浮点数之间的缝
机器精度的定义是: 和「比它大的下一个能表示的浮点数」之间的间隔。对 float64,这个值约为 :
import numpy as np
print(np.finfo(np.float64).eps) # 2.220446049250313e-16
print(np.finfo(np.float32).eps) # 1.1920929e-07 (单精度差得多)它的意义在于框定了单次运算的相对误差。任何一次浮点运算(加减乘除)的结果,和真实值之间的相对误差,都不超过约 :
换算成人话:float64 大约有 1516 位十进制有效数字,8 位。 超出这个精度的位,全是不可信的噪声。一个直观的演示——给 加上半个 ,它「小到」根本改变不了 :float32 只有 7
eps = np.finfo(np.float64).eps
print(1.0 + eps/2 == 1.0) # True — 半个 eps 加上去,等于没加
print(1.0 + eps == 1.0) # False — 一个完整 eps 才够跨到下一个浮点数误差会累积
单次运算误差小到 ,听着可以忽略。但运算成千上万次,误差会累积、放大。最简单的例子——把 0.1 加十次,结果居然不是 1.0:
s = 0.0
for _ in range(10):
s += 0.1
print(s, s == 1.0)
# 0.9999999999999999 False每次加 0.1(本身就存不准)都引入一点舍入误差,十次攒下来,就差出了最后一位。加的次数越多、数据量级差得越大,累积误差越严重。 一个求和上亿个数的科学计算,朴素地一个个加,末尾可能丢掉好几位有效数字。
一门「艺术」:Kahan 补偿求和
累积误差不是没救。有个漂亮的技巧叫 Kahan 补偿求和(compensated summation):它用一个额外变量,追踪每一步被舍掉的那一点点误差,并在下一步把它补回来。
def kahan_sum(nums):
total = 0.0
c = 0.0 # c:记录上一步丢掉的低位误差
for x in nums:
y = x - c # 把上次的误差补进这次
t = total + y
c = (t - total) - y # 算出这次又丢了多少(数学上为 0,浮点下不为 0)
total = t
return total
print(kahan_sum([0.1] * 10)) # 1.0 —— 补偿之后,精确了c = (t - total) - y 这一行是精髓:数学上它恒等于 0,但在浮点运算下,它恰好捕捉到了这一步加法丢掉的低位,留到下一轮补回去。于是长求和的误差从「随次数增长」压到了几乎恒定。
实践中你不必自己写 Kahan——Python 标准库的 math.fsum 和 NumPy 在很多地方已经用了更精巧的补偿 / 成对求和:
import math
print(math.fsum([0.1] * 10)) # 1.0 —— 标准库的精确求和但理解 Kahan 的意义,在于它示范了数值计算的核心思维:误差不是「认命」,而是可以被分析、被追踪、被主动补偿的。 面对一个精度不够的计算,与其换更高精度的类型(慢、也未必够),不如想想能不能重新安排运算,让误差自己抵消——这正是「艺术」二字的分量。后面几乎每一章,都有这种「换个算法,误差就小一个数量级」的例子。
一个实用推论:相对误差 vs 绝对误差
机器精度框定的是相对误差,这提醒我们:判断「算得准不准」,通常要看相对误差,而非绝对误差。
- 算一个约 的量,绝对误差 其实很好(相对才 )。
- 算一个约 的量,绝对误差 是灾难(相对高达 )。
所以上一章的容差比较,严谨版常常是混合容差:abs(a-b) <= atol + rtol*abs(b)(np.isclose 正是这么做的)——绝对容差 atol 兜住接近 0 的情形,相对容差 rtol 兜住大数。「误差多大」永远要问「相对于什么」。
小结与下一章
机器精度 (float64)框定了单次运算的相对误差,对应约 16 位有效数字。误差会随运算次数累积;但它可被主动补偿(Kahan 求和 / math.fsum),这体现了数值计算「重排运算压制误差」的核心思维。判断精度要看相对误差。
累积是误差「慢慢变大」;还有一种误差会瞬间爆炸——两个相近的数一相减,有效数字当场湮灭。这是数值计算的头号杀手,下一章专讲。👉 灾难性抵消:两个相近的数相减。
参考文献
- [1] Nick Higham.《Accuracy and Stability of Numerical Algorithms》, ch. 2-4 —— 舍入误差与求和的权威分析。
- [2] William Kahan.「Further remarks on reducing truncation errors.」1965. —— Kahan 求和原始文献。
- [3] NumPy 文档.「numpy.finfo / isclose.」https://numpy.org/doc/stable/ —— 机器精度与容差比较。