一句话定义
浮点数(floating point)是一套用「符号位 + 指数位 + 尾数位」三段式来近似表示实数的二进制编码规则;IEEE 754 是这套规则的国际标准,BF16(bfloat16)则是它针对 AI 计算推出的 16 位精简版。
原理:把科学计数法搬进芯片
人写数字习惯用科学计数法:1.23 × 10⁸。三个信息——正负号、小数点位置、有效数字。浮点数在二进制里做同一件事:
- 符号位:正还是负
- 指数位:小数点该往哪挪,决定能表示多大、多小的数
- 尾数位:一共有几位有效数字,决定精度
关键约束是:二进制只能精确表示分母为 2 的幂的分数。0.5、0.25、0.125 都没问题,但 0.1 在二进制里是无限循环小数,只能被截断。所以计算机里存的从来不是 0.1,而是「离 0.1 最近的那个可表示的数」。两个这样的近似值相加,误差叠加,就得到了双精度下的 0.30000000000000004。这不是 bug,是编码方式的必然结果。
三种常见格式
| 格式 | 总位数 | 符号 | 指数 | 尾数 | 大致十进制有效位 |
|---|---|---|---|---|---|
| FP32 | 32 | 1 | 8 | 23 | 约 7 位 |
| FP16 | 16 | 1 | 5 | 10 | 约 3 位 |
| BF16 | 16 | 1 | 8 | 7 | 约 2~3 位 |
BF16 的设计思路很直白:把 FP32 的 8 位指数原样保留,只把尾数从 23 位砍到 7 位。于是它的取值范围和 FP32 完全一样,只是变粗糙了。FP16 则相反,精度还行但指数位太少,容易溢出,训练时往往要做损失缩放(loss scaling)来兜底;BF16 不需要这套额外操作,这也是它在训练和推理里更省心的原因。
BF16 为什么够用
三点:一是动态范围没变,不会因为数值太大或太小而溢出;二是神经网络的权重、梯度、激活值本身是统计量,噪声很大,约 1% 的相对误差通常淹没在噪声里;三是矩阵乘法是访存密集型操作,数值位宽减半,显存占用和带宽压力也跟着减半,吞吐自然上去了。代价是累加等敏感环节容易掉精度,所以实践中常保留一份 FP32 的主副本来做累加和参数更新,具体怎么做以所用框架的官方文档为准。
精度损失从哪来
1. 表示误差:十进制小数转二进制时天生的截断。
2. 舍入误差:每次加减乘除都要把结果塞回可表示的网格,误差逐步累积。
3. 类型转换:FP32 转 BF16 要丢掉 16 位尾数。
4. 灾难性抵消:两个非常接近的大数相减,有效数字被大量抹掉,这是最凶险的一种。
对你意味着什么
普通职场人:涉及金额、对账的字段,永远别用浮点类型,改用整数(以「分」为单位)或十进制类型;看到 0.1 + 0.2 的结果时也不用惊讶。
AI 从业者:不要用严格相等去比较浮点数,改用误差容限;排查「训练好好的突然变 NaN」时,优先怀疑溢出、累加精度和抵消这三件事,而不是先怪数据。
