三种格式怎么选
位宽决定了它能表示多大的范围和多少有效数字。三者的结构对比:
- FP16:1 位符号 + 5 位指数 + 10 位尾数,2 字节。最大 65504,最小规格数 6.1e-5。范围小但精度够用,推理场景常用。
- BF16:1 + 8 + 7,2 字节。指数位数和 FP32 完全一样,所以范围与 FP32 相同,但只有 8 位有效数字。训练时梯度容易溢出,用 BF16 更安全。
- FP32:1 + 8 + 23,4 字节。最大约 3.4e38,有效数字 24 位,通用计算的标准格式。
一句话记法:BF16 是「砍掉尾数的 FP32」,FP16 是「砍掉一半指数、保留较多尾数」。 前者保范围,后者保精度。
规格数与非规格数
指数位全 0 且尾数非 0 时,这个数是非规格数(subnormal)。
此时隐含的前导 1 不再存在,数值按 0.尾数 × 2^(1-bias) 计算。
为什么要这样设计?因为如果直接跳到 0,最小的规格数和 0 之间会有一段巨大的空隙, 差值计算会出现诡异的断层。非规格数让下溢变成渐进的,这在硬件上叫渐进下溢(gradual underflow)。 代价是尾数有效位会随着数值变小而减少。
特殊值一览
所有特殊值都靠指数位全 1 来表示,再用尾数区分: 尾数全 0 是无穷,尾数非 0 是 NaN。NaN 再看尾数最高位—— 为 1 是静默 NaN(qNaN),为 0 是信号 NaN(sNaN)。 ±0 则是指数和尾数全都为 0,只靠符号位区分。
常见问题
FP16、BF16、FP32 有什么区别?
三者位宽不同:FP16 是 1+5+10 位,指数 5 位所以范围只有 ±65504,但尾数 10 位、精度还行;BF16 是 1+8+7 位,指数和 FP32 一样是 8 位,所以范围和 FP32 相同(约 ±3.4e38),但尾数只有 7 位,精度很低;FP32 是 1+8+23 位,范围和精度都够,但占 4 字节。AI 推理常用 FP16,训练常用 BF16——因为训练更怕溢出、不怕精度差。
为什么 0.1 转成 FP16 不是精确的 0.1?
0.1 在二进制里是无限循环小数(0.0001100110011…),任何二进制浮点格式都存不下。FP16 只能保存最接近的 0.0999755859375,位模式是 0x2E66。页面上会标出「已舍入」,并显示实际存下来的值。
非规格数(subnormal)是什么?
当数值小到指数已经到最小值时,格式会放弃隐含前导 1,改用「指数全 0 + 尾数直接表示」的方式,这样能继续表示更小的数,代价是有效位数逐渐减少。FP16 的最小规格数是 6.1e-5,最小非规格数是 5.96e-8——非规格数让下溢变成了渐进式的,而不是直接变成 0。
qNaN 和 sNaN 有什么区别?
q 是 quiet(静默),参与运算时继续传播、不报错;s 是 signaling(信号),参与运算会触发无效操作异常。区分方法是看尾数最高位:为 1 是 qNaN,为 0 且尾数非 0 是 sNaN。两者指数位都是全 1。
这个转换和硬件里的行为一致吗?
舍入采用 IEEE 754 默认的「就近舍入、中间值取偶」(round-half-even)。已用 5000 多个随机样本与 IEEE 原生 Float32 逐位比对,结果完全一致。溢出时返回 ±Inf,下溢到非规格数区间时按最小单位量化。
数据会不会上传?
不会。整个转换在浏览器里用 JavaScript 完成,页面没有后端接口,断网也能用。