2. 定点数表示与量化基础

在Simulink中进行定点化设计,第一步不是急着把数据类型改成 fixdt,而是先弄清楚一个定点数到底由哪些信息决定。一个定点数可以理解为“用整数存储、用约定的小数位解释”的数值:硬件里保存的是一串二进制位,而它代表多少,取决于字长、符号位和小数位这三个参数。理解这三者,才能理解后面所有量化误差、溢出和精度损失的来源。

字长、符号与小数位:定点数的三个坐标

字长(Word Length)指存储这个数用了多少位二进制,常见如8、16、32位。符号(Signedness)决定最高位是否作为符号位:有符号数用补码表示,最高位为1表示负数;无符号数则全部位都用于表示非负值。小数位(Fraction Length)是定点数最容易被忽视、却最关键的部分——它规定二进制小数点从最低位向左移动多少位,也就是“用多少位表示小数”。

在Simulink中,这三者通常写成 fixdt(1,16,12) 这样的形式,含义是:有符号、总字长16位、小数位12位。它对应的实际数值范围是 [-8, 8 - 2-12],最小分辨率(量化步长)为 2-12 ≈ 0.000244。也就是说,这个类型能表示的数不是连续的,而是以约0.000244为间隔的一串离散值。

定标关系:若字长为 W、小数位为 F,则分辨率 Δ = 2-F;有符号时整数部分可用位数为 W-F-1,范围约为 ±2W-F-1;无符号时整数部分可用位数为 W-F,范围为 0 到 2W-F-Δ。

Q格式:一种紧凑的定标记法

Q格式是定点数的一种简写约定,常见写法如 Q15、Q1.14、Q3.12。以 Q3.12 为例,它表示有符号数、整数部分3位、小数部分12位,总字长16位。Q15 通常指有符号16位、小数位15位,范围约 [-1, 1),分辨率 2-15 ≈ 3.05e-5。Q格式的价值在于:它把“字长、符号、小数位”压缩成一个短标记,方便在算法文档、寄存器手册和Simulink数据类型之间快速对照。

需要特别注意的是,同一个Q格式在不同工具或文档中可能对应不同字长约定。例如 Q15 在16位DSP语境下是16位,但在某些库中也可能指其他字长。因此在Simulink建模时,最稳妥的做法是始终以 fixdt(signed, wordLength, fractionLength) 的完整形式确认,而不是只依赖Q标记。

量化误差:从连续值到离散值的必然代价

把一个实数 x 映射到定点类型,本质上是找离它最近的、可表示的离散值。这个过程叫量化。量化误差 e = Q(x) - x,其绝对值不会超过半个量化步长,即 |e| ≤ Δ/2。这是定点化中最基本的误差界。

例如,用 fixdt(1,16,12) 表示 0.3000,Δ ≈ 0.000244。0.3000 / Δ ≈ 1228.8,取整后为1229,对应实际值 1229 × Δ ≈ 0.300049。量化误差约为 +0.000049,小于 Δ/2 ≈ 0.000122,符合误差界。这个例子说明:小数位越多,Δ越小,量化越细,但字长也越大,硬件成本越高。

舍入与饱和:两种必须区分的处理策略

当量化结果落在两个可表示值之间时,需要选择舍入方式。Simulink常见舍入模式包括:Floor(向负无穷取整)、Ceiling(向正无穷取整)、Round(四舍五入)、Convergent(银行家舍入,遇0.5取偶)。不同舍入方式带来的误差分布不同:Floor和Ceiling会产生有偏误差,Round和Convergent通常更接近无偏。在反馈回路中,有偏舍入可能累积成直流偏移,这是定点化调试中常见的隐蔽问题。

当数值超出目标类型可表示范围时,需要选择溢出处理:饱和(Saturate)会把结果钳位到最大或最小值;回绕(Wrap)则按补码规则循环,可能把大正数变成负数。工程上通常优先选择饱和,因为它把溢出限制在边界,便于发现和定位;回绕虽然在某些算法中有用,但更容易掩盖问题。

定点数表示与量化处理链 连续实数 x 例:0.3000 量化 Q(x) Δ = 2⁻ᶠ |e| ≤ Δ/2 舍入模式 Floor / Ceiling Round / Convergent 溢出处理 Saturate Wrap fixdt(1,16,12) 的位结构示意 S 符号位 I 整数位 F = 12 位小数位 分辨率 Δ = 2⁻¹² ≈ 0.000244 范围:[-8, 8 - Δ] 总字长 W = 16,符号位 1,整数位 3,小数位 12 量化误差与溢出判断 |e| ≤ Δ/2 → 正常量化 超出范围 → 触发溢出 Saturate 钳位到边界

在Simulink中建立直观认识

在Simulink里验证上述概念,最直接的方式是用 Data Type Conversion 模块配合 Display 或 To Workspace 观察量化前后的差异。例如,构造一个从 -10 到 10 的斜坡信号,经过 fixdt(1,16,12) 转换后,用示波器同时观察原始信号和量化信号:在数值变化平缓处,量化信号呈阶梯状;在接近 ±8 边界处,若输入继续增大,饱和模式下信号被钳在边界,回绕模式下则会出现跳变。

另一个实用做法是使用 fi 对象在MATLAB命令行中直接计算。例如:

a = fi(0.3, 1, 16, 12);
disp(a);          % 显示量化后的实际值
disp(a.delta);    % 显示量化步长
disp(a.range);    % 显示可表示范围
b = fi(9.5, 1, 16, 12, 'OverflowAction', 'Saturate');
disp(b);          % 观察饱和结果
c = fi(9.5, 1, 16, 12, 'OverflowAction', 'Wrap');
disp(c);          % 观察回绕结果

运行后可以看到:a 的实际值约为 0.300049,a.delta 为 0.000244,a.range 为 [-8, 8-Δ]。b 被饱和到 7.999756,而 c 则回绕成一个负数。这个对比能非常直观地说明:同样的输入,不同的溢出策略会得到完全不同的结果,而饱和通常更符合工程直觉。

建模建议:在Simulink中设置定点类型时,优先使用 fixdt(signed, wordLength, fractionLength) 完整形式,并在模型注释中记录每个关键信号的Q格式和预期范围。对于反馈回路中的累加器,建议预留额外整数位(Guard Bits),避免中间结果溢出。

常见误区:只关注字长而忽略小数位,会导致“看起来位数够,实际精度不够”;只设置饱和而不检查范围,会把溢出问题掩盖成边界钳位,反而更难定位。量化误差和溢出是两类不同的问题,必须分别观察、分别处理。

掌握字长、符号、小数位、Q格式、量化误差、舍入与饱和这七个概念之后,就具备了阅读和设计定点数据类型的语言基础。下一步是在Simulink中把这些参数真正落到信号线上,观察它们对系统行为的影响。