☰ inferserver 的数学基础

14. 浮点数与误差:为什么容差是 1e-6

14.1 float32 的精度

IEEE-754 float32:1 位符号、8 位指数、23 位尾数。规格化数的有效位数是 24 位(隐含前导 1),所以 1.0 附近的机器 epsilon 是

\[ \varepsilon_{\text{mach}}=2^{-23}\approx1.19\times10^{-7}, \]

而单次舍入的单位 roundoff 是 \(u=2^{-24}\approx5.96\times10^{-8}\)。粗略地说,float32 能保证 7 位十进制有效数字。

bf16 只有 7 位尾数(+1 隐含位 = 8 位),roundoff 是 \(2^{-9}\approx0.002\),精度差很多,但指数范围和 float32 一样。Qwen3 权重以 bf16 存储,计算时转成 float32,所以“权重精度”和“累加精度”是两件事。

14.2 误差从哪来

14.3 本项目为什么用 1e-6

跨实现对比(Go vs HuggingFace/PyTorch)时,累加顺序、向量化指令、float64 中间量都不同,逐位相等不现实。仓库的验收用相对误差

\[ \text{rel}=\frac{\|a-b\|}{\|b\|}, \]

阈值取 \(10^{-6}\);实际 tiny 模型上约 \(10^{-7}\),真实 0.6B 上约 \(10^{-5}\sim10^{-6}\)(见 inferserver 的 FORWARD.md §8.3)。如果一个“看起来很小”的误差出现在关键张量上(比如 embed 不是精确 0),通常说明权重加载或 dtype 转换错了,而不是浮点噪声。

实用判断

相对误差 \(10^{-7}\) 量级:正常舍入;\(10^{-3}\) 量级:多半是公式/索引错了;符号相反或量级差很多:接线错误。不要用“输出还能看”来判断正确性——LLM 对很多错误非常宽容。