Skip to content

数值精度与表示范围:从 FP32 到 INT2 的数据类型速查

深度学习的训练与推理会用到很多种数值类型,从高精度的 FP32(float32)一路到低比特量化的 INT2。本文用最直白的语言回答三个问题:每种类型能表示多大的范围?能保留多少精度?这些最大值、最小值是怎么算出来的? 看懂它们,是理解模型量化、算子优化和避免数值溢出(比如 Softmax 溢出)的第一步。

1. 浮点数的表示原理

IEEE 754 浮点数用三个字段表示一个数:

value=(1)s×(1.f)×2ebias

这个公式就是计算机里的"科学计数法":我们熟悉的科学计数法是 1.23 × 10⁵——"有效数字 × 10 的幂次";浮点数只不过把底数从 10 换成了 2,把 1.23 换成了 1.f。其中 1.f 决定精度(有效数字),指数 2ebias 决定范围(量级)。四个字段各司其职:

  • 符号位 s(1 位):决定正负。
  • 指数位 e:决定范围——能表示的最大值和最小绝对值。
  • 尾数位 f:决定精度——十进制有效数字的个数。
  • bias:指数偏移,使指数可取负值。

三个字段按位排列,各自对"范围"与"精度"的决定作用如下图所示:

两个关键结论:

  1. 指数位越多,范围越大。例如 FP32 与 FP16 各有 8 位与 5 位指数,前者最大到 1038 量级,后者仅到 65504。
  2. 尾数位越多,精度越高。十进制有效数字约等于 log102×(尾数位数+1)——换句话说,尾数每多约 3.3 位,十进制有效数字就多 1 位(因为 1/log1023.32)。

另外区分两种数值:

  • 正规数(normal):完整精度,尾数隐含前导 1。
  • 非正规数(subnormal):指数域全 0,精度略降,但能填补正规数最小值到 0 之间的空隙。

沿正半轴看,各类数值的分布与指数域的编码一一对应:

衡量精度最常用的指标是机器精度(machine epsilon)ε,即 1.0 与下一个可表示数之间的间隔:

ϵ=2尾数位数

它代表相对精度——任何实数的相对表示误差一般不超过 ϵ/2

2. 最大最小值是怎么算出来的

这一节回答一个问题:表格里的最大值、最小值,到底是怎么从"位宽"一步步算出来的。 记不住公式没关系,先记住一句话:

浮点数 = 精度 × 规模:尾数决定"数字长什么样",指数决定"数字有多大"。最大值,就是把"精度拉满"和"规模拉满"相乘。

2.1 浮点数的最大值:65504、3.40e38 怎么来的

回到浮点数的公式:

value=(1)s×1.f×2ebias

它由两部分拼成:

  • 1.f(尾数)——决定"数字本身"。尾数有几位,就能写出几位精度的数字,FP32 的 23 位尾数就是这样来的。
  • 2^(e−bias)(指数)——决定"放大多少倍"。指数大,数就大;指数小,数就小。

最大值的思路非常直白:尾数取最大(所有位全 1),指数取最大(能用的最大指数),两者相乘。

尾数全 1 是多大?二进制里 1.111…1 约等于 22m,也就是"非常接近 2,但永远差一点"。指数能用到多大?注意指数域全 1(如 FP32 的 11111111)必须留给 ±∞ 和 NaN,不能用来表示普通数字,所以能用的最大指数恰好是 bias(FP32 是 127,FP16 是 15)。于是最大值公式就是:

max=(22m)×2bias

一个个代入,结果和总览表完全一致:

类型最大指数 Emax最大尾数最大值
FP321272223≈ 3.40 × 10³⁸
FP161522101.999 × 32768 = 65504
BF16127227≈ 3.39 × 10³⁸
FP8 E5M2152221.75 × 32768 = 57344
FP8 E4M381.75(尾数全 1 留 NaN)1.75 × 2⁸ = 448
FP4 E2M122211.5 × 2² = 6

"精度拉满 × 规模拉满"用图表示就是:

不过有两个例外,因为硬件对"保留位"的处理不一样,不能直接套"尾数全 1":

  • FP8 E4M3:这个格式没有 ±∞,所以指数域 1111 还能拿来用(相当于 E = 8);但尾数 111 留给了 NaN,最大尾数只能到 1.110 = 1.75。于是最大值 = 1.75 × 2⁸ = 448
  • FP4 E2M1:bias = 1,指数域 11 相当于 E = 2,最大尾数 1.1 = 1.5。于是最大值 = 1.5 × 2² = 6

2.2 浮点数的最小正数:能小到什么程度?

求最小值和求最大值正好相反:指数取最小,尾数也取最小。

先把每种类型的"最小正数"都列出来,再看它们怎么来的:

类型最小正正规数(指数域 = 1)最小正非正规数(指数域 = 0)非正规数还能再小 2m
FP3221261.18×103821491.40×1045223 ≈ 840 万倍
FP162146.10×1052245.96×108210 = 1024 倍
BF1621261.18×103821339.18×104127 = 128 倍
FP8 E4M326 = 0.01562529 ≈ 1.95 × 10⁻³23 = 8 倍
FP8 E5M22146.10×105216 ≈ 1.53 × 10⁻⁵22 = 4 倍
FP4 E2M120 = 1.021 = 0.521 = 2 倍

这两列数字其实只差一个动作:

  • 最小正正规数:把指数域调到 1(正规数允许的最小指数),尾数取 0,得到 21bias。FP32 就是 21127=21261.18×1038
  • 最小正非正规数:正规数已经小到极限了,还想更小怎么办?答案是牺牲一点精度换更小的数——把指数域全设 0,得到 2m×21bias,相当于在正规数基础上再小 2m。FP32 就是 223×2126=21491.4×1045

打个比方:正规数像"必须写成 1.xxx"的科学计数法,指数已经小到不能再小;非正规数就是把 1.xxx 改成 0.00xxx,用丢精度换来更小的数。这正是第 1 节数轴图上"非正规数填补 0 与最小正规数之间的空隙"的意思。

2.3 整数类型的范围:没有指数,全靠位宽

整数比浮点简单得多——没有指数,每一位都在"数数",所以范围完全由位宽决定:

  • 有符号 N 位:最高位当正负号,剩下 N−1 位用来数数,最多数到 2N11,所以范围是 [2N1, 2N11]
  • 无符号 N 位:N 位全部用来数数,范围是 [0, 2N1]

位分配示意:

用 INT8 举个例子:8 位里拿 1 位当符号,剩 7 位数数,最多数到 127,所以范围是 [−128, 127];UINT8 的 8 位全用来数数,最多到 255。

整数还有一个浮点做不到的好处:范围内的每个整数都能精确表示,步长永远是 1——没有"1.9 和 2.0 之间还有别的数"这种事。这也是为什么数字超过 2048 之后,FP16 反而数不出每一个整数了。

3. 浮点类型一览

先用一张表把 6 种浮点类型的全貌看全,再逐类说明它们在深度学习里各自的用武之地。

3.1 总览表

类型位宽布局(符号/指数/尾数)最大正数最小正正规数最小正非正规数十进制有效数字
FP32(单精度)321 + 8 + 23≈ 3.40 × 10³⁸≈ 1.18 × 10⁻³⁸≈ 1.40 × 10⁻⁴⁵~7 位
FP16(半精度)161 + 5 + 1065504≈ 6.10 × 10⁻⁵≈ 5.96 × 10⁻⁸~3 位
BF16(bfloat16)161 + 8 + 7≈ 3.39 × 10³⁸≈ 1.18 × 10⁻³⁸≈ 9.18 × 10⁻⁴¹~2 位
FP8 E4M381 + 4 + 34480.015625≈ 1.95 × 10⁻³~1 位
FP8 E5M281 + 5 + 257344≈ 6.10 × 10⁻⁵≈ 1.53 × 10⁻⁵~1 位
FP4 E2M141 + 2 + 161.00.5<1 位

小提示:有符号类型(无论浮点还是整数)的最负值 = 最大正数取负——FP16 是 −65504、INT8 是 −128;无符号类型最小值恒为 0。

3.2 各类型要点

FP32(float32):标准的单精度浮点,约 7 位十进制有效数字,最大约 3.4 × 10³⁸。主权重、梯度累加等对精度要求最高的场合使用。

FP16(float16):Tensor Core 加速的常用半精度,约 3 位有效数字。最大只有 65504——这就是 Softmax 需要"安全 Softmax"的原因:e11.16.6×104 已超过 65504,直接计算会溢出,必须先减去行最大值。详见 FlashAttention 详解

BF16(bfloat16):与 FP32 相同的 8 位指数(因此范围与 FP32 一致),但尾数仅 7 位(约 2 位有效数字)。混合精度训练的主流选择:范围无忧、显存减半,代价是精度降低。

FP8:8 位浮点有两种硬件变体:

  • E4M3(4 指数 + 3 尾数):最大 448,精度略高、范围小,适合权重与前向激活
  • E5M2(5 指数 + 2 尾数):最大 57344,范围大、精度低,适合反向传播的梯度

FP4(E2M1):4 位浮点(1 符号 + 2 指数 + 1 尾数,bias 1),共 16 种位模式(±0 重合,实际 15 个不同数值),正数取值集为 {0,0.5,1,1.5,2,3,4,6}最大为 6,最小正非正规数为 0.5。相比同为 4 位的 INT4,FP4 的小值更密集(按指数分布),能更好保留权重中的小值异常(outlier),是 NVIDIA Blackwell 等新一代硬件上 4 比特量化的首选。

4. 精度详解:机器精度与有效数字

衡量精度主要看两个视角:机器精度 ε(相对误差的刻度,ε 越小数值越"密"、越精确)和十进制有效数字(最直观的"大概能信几位")。下面逐个类型算出来。

4.1 各类型精度明细

类型尾数位机器精度 ε十进制有效数字相对误差上限(≈ ε/2)
FP3223≈ 1.19 × 10⁻⁷~7 位≈ 6 × 10⁻⁸
FP1610≈ 9.77 × 10⁻⁴~3 位≈ 5 × 10⁻⁴
BF167≈ 7.81 × 10⁻³~2 位≈ 4 × 10⁻³
FP8 E4M330.125~1 位≈ 6 × 10⁻²
FP8 E5M220.25~1 位0.125
FP4 E2M110.5<1 位0.25

机器精度 ϵ=2尾数位数 即 1.0 与下一个可表示数的间隙:尾数越少,ε 越大、相对精度越低。从 FP32 到 FP4,ε 从约 107 一路增长到 0.5——低比特浮点的每次运算都可能引入百分之几到高达百分之二十五(FP4)的相对误差。

4.2 绝对精度随数值增大而变差

先记住一个反直觉的结论:相对精度固定(由 ε 决定),但绝对精度会随数值变大而变差。 打个比方:就像一把刻度为"总长度的 0.1%"的尺子——量 1 米时误差只有 1 毫米,量 1 公里时误差就变成 1 米。数越大,相邻两个可表示数之间的"缝隙"越大。

对位于区间 [2k,2k+1) 的值,相邻可表示数的间隔(即最后一位单位 ULP)为:

ULP=2k尾数位数

具体例子:

  • FP16 在 1.0 附近:相邻可表示数的间隙约 2100.001(约 0.1% 的相对精度);但在最大值 65504 附近,间隙达 25=32,连整数都无法一一表示。
  • FP16 能精确表示的最大整数是 2048211),超过后开始出现"空洞"。
  • FP32 在最大值约 3.4×1038 附近:间隙高达约 21042×1031——大数做加法时,较小的增量会直接丢失。

4.3 整数类型的精度

整数类型的"精度"即步长,恒为 1:在各自范围内,每一个整数都能被精确表示,不存在舍入误差。这是相对浮点的一大优势——例如 INT8 在 −128~127 内绝对精确,而 FP16 在绝对值超过 2048 时反而做不到。更大的位宽(INT16/INT32/INT64)只是扩展表示范围,步长依然是 1,精度不变。

5. 整数类型

下表汇总各整数类型的完整范围。整数没有指数、每一位都在"数数",所以范围只由位宽决定(推导见第 2.3 节):

类型位宽有符号范围无符号范围步长十进制有效数字
INT6464−9.2 × 10¹⁸ ~ 9.2 × 10¹⁸0 ~ 1.8 × 10¹⁹1~19 位
INT3232−2.1 × 10⁹ ~ 2.1 × 10⁹0 ~ 4.3 × 10⁹1~10 位
INT1616−32768 ~ 327670 ~ 655351~5 位
INT88−128 ~ 1270 ~ 2551~2 位
INT44−8 ~ 70 ~ 151~1 位
INT22−2 ~ 10 ~ 31<1 位
  • INT64:需要超大整数(如超长上下文、超大矩阵的元素总数)时使用,有符号范围 −9223372036854775808 ~ 9223372036854775807。注意 GPU 上 INT64 算术比 INT32 慢得多,能不用就不用。
  • INT32:CPU/GPU 上默认的索引与计数类型(CUDA 的 int、Python 的 int),有符号范围 −2147483648 ~ 2147483647,足以覆盖绝大多数序列长度与矩阵维度。
  • INT8:W8A8 权重量化的主流,配合 scale/zero_point 把浮点权重映射到整数网格。
  • INT4:W4A16 等低比特量化(如 GPTQ、AWQ)常用,只有 16 个取值(约 1 位十进制有效数字)。
  • INT2:极端低比特研究(双比特/三值量化),仅有 4 个取值,精度损失大,通常只在特定场景探索。
  • UINT(无符号):UINT8 0~255、UINT4 0~15、UINT2 0~3,通常配合非对称量化(zero-point)使用。

FP4 与 INT4 的关键区别:同为 4 位,INT4 的取值均匀分布(−8 ~ 7),适合分布接近均匀的权重;FP4 的取值按指数分布(0、±0.5、±1、±1.5、±2、±3、±4、±6),小值更密集,能更好地保留权重中的小值信息,因此在新一代硬件上更受青睐。整数类型的十进制有效数字约等于 log10(取值个数)(如 INT8 为 log102562.4)。

6. 精度与范围的权衡直觉

前面讲了原理和数字,这一节总结三条"实战直觉",帮你快速决定该用哪种类型。

  • 同一格式内,指数与尾数互相竞争:同样 16 位,BF16 用更多位给指数(范围大、精度低),FP16 用更多位给尾数(精度高、范围小)。两种布局对照如下:

将第 3 节总览表中的 FP16 与 BF16 两行与上图对照,就能看到:位宽相同的两种半精度,只是把有限的 16 位按不同比例分给了"范围"与"精度"

  • 训练:FP32 保存主权重,FP16/BF16 计算激活与梯度,FP8 进一步加速训练。
  • 推理:FP16/BF16 为基座,FP4/INT8/INT4 量化权重以压缩显存、提升吞吐,INT2 属前沿探索。
  • 溢出风险:FP16 范围窄(max 65504),Softmax 前的 Logits 极易溢出,这正是 Online Softmax 与 FlashAttention 用"减去最大值"规避的原因。

6.1 验证脚本

用 Python 快速打印各类型的极限值:

python
import numpy as np
import math

print("FP16:", np.finfo(np.float16))   # max ≈ 65504, eps ≈ 9.8e-4
print("FP32:", np.finfo(np.float32))   # max ≈ 3.4e38, eps ≈ 1.2e-7

# 机器精度 ε = 2^(-尾数位数);十进制有效数字 ≈ (尾数位数 + 1) × log10(2)
for name, man in [("FP32", 23), ("FP16", 10), ("BF16", 7), ("E4M3", 3), ("E5M2", 2), ("FP4", 1)]:
    eps = 2 ** (-man)
    digits = (man + 1) * 0.3010
    print(f"{name}: ε = {eps:.3g}, 约 {digits:.1f} 位十进制有效数字")

# 用公式复算最大值:max = (2 - 2^(-尾数位)) × 2^bias
for name, exp, man in [("FP32", 8, 23), ("FP16", 5, 10), ("BF16", 8, 7), ("E5M2", 5, 2)]:
    bias = 2 ** (exp - 1) - 1
    max_val = (2 - 2 ** (-man)) * 2 ** bias
    print(f"{name}: max = (2 - 2^-{man}) × 2^{bias}{max_val:.3g}")

# 整数范围:有符号 [-2^(N-1), 2^(N-1) - 1]
for n in [2, 4, 8, 16, 32, 64]:
    print(f"INT{n}: [{-(2 ** (n - 1))}, {2 ** (n - 1) - 1}]")

# FP4 的完整取值集(E2M1)
print("FP4 取值集:", [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0])

# 整数类型的十进制有效数字 ≈ log10(取值个数)
for name, n in [("INT64", 2**64), ("INT32", 2**32), ("INT16", 2**16),
                ("INT8", 2**8), ("INT4", 2**4), ("INT2", 2**2)]:
    print(f"{name}: 约 {math.log10(n):.1f} 位十进制")

总结

关注点结论
精度由什么决定尾数位数(机器精度 ϵ=2尾数位数
范围由什么决定指数位数
最大最小值怎么算浮点:最大尾数 × 最大指数幂((22m)×2bias);整数:有符号 [2N1, 2N11]
FP16 最大正数65504(Softmax 需防溢出)
BF16 与 FP32 的关系范围相同,精度仅 ~2 位
FP8 两种变体的分工E4M3 给权重/激活,E5M2 给梯度
FP4 的取值集{0, ±0.5, ±1, ±1.5, ±2, ±3, ±4, ±6},最大 6
整数类型的精度步长恒为 1,范围内整数绝对精确
标准整数类型INT32/INT64 用于索引与计数,INT32 是 CUDA 默认;位宽越大范围越广、精度不变
机器精度 ε 跨度FP32 ≈ 1.2×10⁻⁷ → FP4 为 0.5

相关链接

参考

  1. IEEE 754 浮点数标准 — Wikipedia
  2. Micikevicius, P., et al. (2022). FP8 Formats for Deep Learning. arXiv:2209.05433.
  3. FP8: 8-bit floating point for fast training and inference — Zero Entropy
  4. FP8 Quantization — vLLM Docs
  5. numpy.finfo 文档

Maintained by Robin