3. 定点数设计与数值精度控制
在嵌入式部署中,浮点运算往往意味着更高的资源占用、更大的功耗,甚至在某些 MCU 或 FPGA 目标上根本不可用。定点数设计的目标,是在满足精度要求的前提下,用整数运算近似表达实数运算。MATLAB 的 Fixed-Point Designer 提供了 fi 对象作为定点建模的核心载体,它把“数值”和“格式”绑定在一起,使量化误差、溢出行为在算法开发阶段就可被观察和控制。
一、fi 对象与定点类型定义
fi 对象的基本构造形式为 fi(value, signed, wordLength, fractionLength)。其中 signed 取 1 或 0 表示有符号/无符号,wordLength 是总位宽,fractionLength 是小数位宽。定点格式通常写作 fixdt(signed, wordLength, fractionLength),例如 fixdt(1,16,12) 表示有符号 16 位、其中 12 位小数,其分辨率为 2-12 ≈ 0.000244,可表示范围约为 [-8, 8)。
需要区分两个关键量:分辨率由小数位宽决定,动态范围由整数位宽(wordLength − fractionLength)决定。二者此消彼长:增加小数位提升精度但压缩范围,减少小数位扩大范围但降低精度。设计定点格式,本质是在这两者之间做权衡。
a = fi(1.625, 1, 8, 4); % 有符号8位,4位小数
disp(a.bin); % 二进制表示
disp(a.realmax); % 最大可表示值
disp(a.realmin); % 最小可表示值
disp(a.resolution); % 分辨率 = 2^-4
T = fixdt(1, 16, 12); % 定义定点类型
b = fi(0.3, T); % 按类型量化
disp(b.double); % 量化后的实际值
二、量化影响分析
把一个浮点值写入定点格式时,会发生两类误差:量化误差(舍入到最近可表示值)和溢出误差(超出范围时的饱和或回绕)。fi 默认采用“最近舍入、饱和溢出”,但可通过 fimath 修改。例如 fimath('RoundingMethod','Floor','OverflowAction','Wrap') 会改为向下取整和回绕。
量化误差的绝对上界是半个分辨率(最近舍入时)。若某信号分辨率 2-12,则单次量化误差不超过约 1.22e-4。但误差会在运算中累积:一次乘加链中,每个中间结果若都被量化,误差会叠加。因此定点设计不仅要看输入格式,还要看中间变量和累加器的格式。
溢出则更危险。饱和溢出把结果钳位到最大/最小值,误差大但可预测;回绕溢出会让大正数突然变成大负数,在控制回路中可能引发剧烈振荡。工程上通常对累加器保留足够整数位,对输出级采用饱和。
三、数值溢出与精度验证方法
验证定点设计是否合格,不能只看单个数值,而要看整段信号在定点下的表现。常用方法有三类:
- 范围统计:用
min/max或fi的realmax/realmin对比信号实际动态范围,判断整数位是否足够。 - 误差度量:将定点结果与浮点参考结果相减,计算最大绝对误差、均方根误差(RMSE)和信噪比(SNR)。
- 溢出检测:通过
fimath的日志或fi的OverflowAction观察是否发生饱和/回绕,并统计发生次数。
下面给出一个可复用的验证脚本示例,用于评估某滤波器系数定点化后的误差:
% 浮点参考
x = sin(2*pi*0.05*(0:999));
b_ref = fir1(31, 0.2);
y_ref = filter(b_ref, 1, x);
% 定点系数与定点运算
T = fixdt(1, 16, 14);
b_fix = fi(b_ref, T);
y_fix = filter(b_fix, 1, x);
% 误差度量
err = double(y_fix) - y_ref;
maxAbsErr = max(abs(err));
rmse = sqrt(mean(err.^2));
snr = 20*log10(norm(y_ref)/norm(err));
fprintf('MaxErr=%.3e RMSE=%.3e SNR=%.2f dB\n', ...
maxAbsErr, rmse, snr);
判断标准因应用而异:音频处理通常要求 SNR 大于 60 dB,控制回路更关注最大误差是否影响稳定性。若误差超标,可增加小数位、改用更高位宽,或对关键中间变量单独指定格式。
四、定点格式设计流程
综合以上内容,一个可操作的定点设计流程如下:先采集或仿真得到信号的浮点动态范围,据此确定整数位宽;再根据可接受的量化误差确定小数位宽;然后为每个中间变量和累加器单独指定格式;最后用浮点参考对比,检查最大误差、RMSE、SNR 和溢出次数,不达标则回到格式调整。
图中把“格式定义—中间变量—验证—判断”串成闭环:若验证不达标,回到格式调整;达标则输出可用于代码生成的定点格式。这样,定点设计就从“凭经验试”变成“有度量、可迭代”的工程过程。
fi 对象的 fimath 与目标处理器行为一致。例如某些 DSP 默认饱和溢出,而通用 C 代码默认回绕,若不一致会导致仿真通过但上板异常。最后强调一点:定点精度控制不是一次性任务。算法结构变化、输入信号范围变化、滤波器阶数调整,都可能让原有格式不再适用。把范围统计和误差度量脚本保留下来,每次修改后重新运行,是保证定点设计长期可靠的低成本手段。