30 秒看懂 Q 格式
一个 16 位寄存器想存 0.3,芯片上没有浮点单元怎么办?把刻度打密一点:
规定小数点固定在 bit15 和 bit14 之间(1 位符号 + 15 位小数,就是 Q15),
存 round(0.3 × 32768) = 9830。读出来再乘 1/32768,得到 0.29998779296875——
差的那 0.0000122,就是量化误差,最多半个 LSB。
所以 Q 格式的一切参数都能从两个数字推出来:小数位 n 决定分辨力(1 LSB = 2⁻ⁿ), 总位宽减去小数位决定范围。n 给得越多,0.3 越准,但能表示的上限越小—— Q15 连 1.0 都存不下(会饱和到 0.999969),这就是范围和精度之间的交换。
和浮点转换器是什么关系
浮点的小数点是浮动的,靠指数换来动态范围;定点的小数点是焊死的,换来的是简单和确定性。 如果你在做的是 DSP 循环、电机控制、或者把模型权重搬到没有 FPU 的 MCU 上, 用的是这个工具;如果在看 AI 推理引擎的 FP16 / BF16,用 浮点数转换。两者底层都是「比特怎么解释成数」,可以对照着看。
常见问题
Q15、Q0.15、s1.15 是同一个东西吗?
是。都是 16 位、1 个符号位加 15 个小数位,只是叫法不同:TI 手册写 Q15,ARM 手册写 Q0.15,DSP 手册常写 s1.15。本工具统一用 Qm.n:1 个符号位、m 个整数位、n 个小数位,总位宽 = m + n + 1;无符号格式写成 uQm.n。
为什么 0.3 存进去再读出来会差一点点?
定点数只能表示 2^-n 的整数倍,而 0.3 在二进制里是无限循环小数。Q15 下最近的可用值是 9830/32768 = 0.29998779296875,差了约 0.4 个 LSB。这叫量化误差,四舍五入时不超过半个 LSB,且存读多少遍都不会自己消失。
溢出时应该饱和还是回绕?
看语义。饱和(本工具的处理方式)把结果钳到最大或最小值,适合音频、电机这类「超了就顶住」的物理量;回绕(C 里的自然溢出)会让 0.9999 加 0.0002 变成接近 -1 的数,在信号处理里通常是灾难。DSP 一般有专门的饱和指令,通用 CPU 上要自己写 if。
定点乘法为什么不能直接乘完就用?
两个 Qm.n 相乘,小数位会翻倍:Q15 乘 Q15 得到 Q30,要再右移 15 位才是 Q15。而且 16×16 位的完整乘积有 32 位,中间结果必须放进双倍位宽的变量,比如 int32_t c = (int32_t)(((int64_t)a * b) >> 15)。在 16 位平台上直接写 a * b >> 15 会先溢出再移位,结果就错了。
什么时候用定点,什么时候用浮点?
芯片没有 FPU 时,定点快一个数量级;需要逐位可复现(不同编译器浮点结果可能不同)时定点占优;量程动态范围大,比如 0.001 和 100000 同时出现时,浮点更合适。现代 MCU 大多带 FPU,新项目可以优先浮点;定点主要留在 DSP、电机控制和老的音频通路里。