53 / 63 · 概念
定点数、舍入与饱和
用 W、F 定义数值范围,设计基于 guard、sticky 的偶数舍入及舍入后饱和。
课时内容可免费阅读,选课后可保存学习进度。
先修知识:应理解 signed 加法和乘法的位宽、算术右移与溢出。
为位串约定小数点位置
定点数定点数 预先固定小数点位置,并对整数位串施加固定比例的数值表示方法。必须同时规定位宽、符号属性和小数位数。将整数 X 解释为 。硬件并不存储一个“小数点比特”。本课规定 signed 总位宽 W 包含符号位,小数位数为 F。不同文献的 Q 记法对整数部分是否包含符号位有不同约定,因此在接口中同时写明 W、F 更清楚。
W=8、F=4 时,间隔为 1/16,范围为 -8~7.9375。在此约定下,0x18 不是整数 24,而是 1.5。同样的 0x18 若按 F=3 解释,则为 3。线路连接正确并不意味着数值含义正确,因此必须记录每个端口的比例因子。
相加前必须先对齐小数点。若 F 不同,可将较小 F 的一方充分扩宽后左移,或按规定舍入方式缩减较大 F 的一方。同一 W、F 的两个数要精确相加,需要 W+1 位并保持 F 不变。乘法则先将原始整数相乘,再把小数位数相加。
两个八位 F=4 数的完整乘积为 signed 十六位 F=8。若要再输出八位 F=4,应先减少四个小数位,再检查 signed 八位范围。若先把乘积截为八位,后续饱和电路就无法知道原始大小。
截断与舍入的区别
丢弃 d 位的算术右移为 ,负数同样向下取整。例如 ,精确商为 -1.5,但 q=-2,与向零截断不同。不能假设对正数适用的“直接取高位”,对负数也有相同误差特性。
这里采用就近舍入,恰好位于中点时取偶数整数,即 round-to-nearest, ties-to-even。将舍弃区域的最高位记为 guard g,其余位的 OR 记为 sticky t,保留商 q 的最低位记为 l。
g=0 表示不足一半,保持原值;g=1、t=1 表示超过一半,应进位;g=1、t=0 表示恰好一半,只在 q 为奇数时加 1。负数也用同一规则,因为使用向下取整的商 q 及余数 时,总有 。
例如 -24 除以 16 得到 -1.5,q=-2 为偶数,所以保留 -2;-40 除以 16 得到 -2.5,q=-3 为奇数,所以加 1 得到 -2。“负数一律减 1”这样的额外规则反而错误。未饱和时,最终数值误差不超过输出 LSBLSB 权重最低的比特。应同时检查表示方向与索引规则。了解更多 的一半。ties-to-even 不保证任意输入分布的平均误差都为 0,但可减小将所有中点都朝同一方向舍入的偏差。
将十六位 F=8 的原始值 P=2040 转为八位 F=4。127.5 先舍入为偶数 128,再限制到 signed 八位最大值 127。直到最后存储之前,都必须保留较宽位宽。
- 输入 P=2040: 十六位 · F=8
- 缩减小数位数: 2040 / 16 = 127.5
- ties-to-even: 舍入后原始值 = 128
- 饱和后存储: raw 127 → 7.9375
完成舍入后再做饱和
饱和将大于最大值的结果限制为最大值、小于最小值的结果限制为最小值。与回绕不同,大正数不会翻成负数。但对已经缩窄并回绕的值,无法再正确实施饱和。
以下完整组合模块将 signed 十六位 F=8 转为 signed 八位 F=4。d 固定为 4,因此 guard 为 p[3],sticky 为 p[2:0]。舍入增量与范围比较都在十七位 signed 域中完成,最后才取八位。
module requantize (
input logic signed [15:0] p,
output logic signed [7:0] y,
output logic saturated
);
logic signed [16:0] wide, q, rounded;
logic inc;
always_comb begin
wide = {p[15], p};
q = wide >>> 4;
inc = p[3] & ((|p[2:0]) | q[0]);
rounded = q + (inc ? 17'sd1 : 17'sd0);
saturated = 1'b0;
if (rounded > 17'sd127) begin
y = 8'sh7f;
saturated = 1'b1;
end else if (rounded < -17'sd128) begin
y = 8'sh80;
saturated = 1'b1;
end else begin
y = rounded[7:0];
end
end
endmoduleP=2040 时,P/16=127.5,先舍入到偶数 128,再饱和到 127,最终值为 7.9375。若只检查舍入前的 q=127,就会漏掉此次越界。saturated 表示数值超出范围而被限制,并不只是丢失了低位小数。
推广为通用模块时,需分别处理 d=0 没有舍弃位、d=1 没有 sticky 区域的情况。仅把越界的部分选择藏在条件语句内,可能仍无法解决展开阶段的错误。
舍入与饱和会增加加法器、比较器、MUX多路选择器 根据选择信号,把多个输入之一连接到输出的组合电路。必须规定选择位与输入编号的对应关系。了解更多的成本与延迟,且应作为独立策略选择。AMD 定点格式文档也区分这些策略。累加多项时,应先决定是否保留中间位宽、最后只转换一次。每一级都饱和可能破坏加法结合律,每一级都舍入则可能重复累积小误差。
自己试试
将 signed 十六位 F=8 输入 P 转为 signed 八位 F=4。P 分别为 24、40、−24、−40、2040 时,求向下取整的商 q、guard/sticky、舍入后整数、最终 raw 输出及实际值。对 2040 若先检查饱和,会出现什么问题?
阅读解释
所有输入的低四位均为 1000,因此 guard=1、sticky=0。P=24:q=1(奇数)→2,raw=2,实际值为 0.125。P=40:q=2(偶数)→2,raw=2,实际值为 0.125。P=−24:q=−2(偶数)→−2,raw=−2,实际值为 −0.125。P=−40:q=−3(奇数)→−2,raw=−2,实际值为 −0.125。P=2040:q=127(奇数)→128,超出 signed 八位最大值,所以 raw=127、实际值=7.9375、saturated=1。若先只检查 q=127,再在八位范围内加 1,就可能回绕成 0x80 并输出 −8。必须在较宽的 signed 值中完成增量与比较。