前言
在之前的文章介绍了定点数为什么需要舍入,以及一种常见的舍入模式:四舍五入round。今天我们再来看看另外一种舍入模式:向下取整floor。
10进制数的floor
floor:也叫 向下取整 或 向负无穷方向取整。它的舍入方式是数据往负无穷的方向,舍入到最近的整数,比如1.75 floor到1,-0.25 floor到-1等。以-2到1.75之间的16个数据(步长0.25)为例,它们的floor结果是这样的:
从上图可以看到:
- 正数的floor,就是把小数部分(或者约定精度外的部分)丢掉。例如1.5 >> 1,0.5 >> 0 等
- 负数的floor,就是把小数部分(或者约定精度外的部分)丢掉然后再减1。例如 -1.5 >> -1-1>>-2,-0.5 >> 0-1>>-1 等
- 0的floor,就是把小数部分丢掉
2进制数的floor
2进制数的floor和10进制的floor类似,但是对于负数部分的处理是不同的。以Q4.2格式的定点数(字长4位,小数2位的有符号数)为例,对于小数部分的处理:
- -2(d) = 10_00(b) floor后的值为 -2,等价于 10,即舍弃小数部分
- -1.75(d) = 10_01(b) floor后的值为 -2,等价于 10,即舍弃小数部分
- -1.5(d) = 10_10(b) floor后的值为 -2,等价于 10,即舍弃小数部分
- -1.25(d) = 10_11(b) floor后的值为 -2,等价于 10,即舍弃小数部分
- -1(d) = 11_00(b) floor后的值为 -1,等价于 11,即舍弃小数部分
- -0.75(d) = 11_01(b) floor后的值为 -1,等价于 11,即舍弃小数部分
- -0.5(d) = 11_10(b) floor后的值为 -1,等价于 11,即舍弃小数部分
- -0.25(d) = 11_11(b) floor后的值为 -1,等价于 11,即舍弃小数部分
由此可以看出,不同于10进制数的floor,2进制数对于负数的floor处理也是直接舍弃掉小数部分。正数的floor自然也是直接舍弃小数部分,因为这个操作等价于减掉小数部分的值,和向下取整的操作契合。
所以,2进制数的floor就很好实现了,只要把小数部分(或者指定精度外的部分)舍弃就行,多说一句,floor操作不需要消耗任何逻辑资源,因为它的实现就是单纯的重新连线。
下面以 用floor的方式来实现Q4.2格式定点数转Q2.0格式定点数为例,Verilog代码如下:
module test(
input [3:0] data_4Q2, //有符号数,符号1位,字长4位,小数2位
output [1:0] data_2Q0 //有符号数,符号1位,字长2位,小数0位
);
assign data_2Q0 = data_4Q2[3:2]; //直接舍弃低位(即整个小数部分)
endmodule
因为一共只有16个数,所以我们可以用穷举的方式来测试,TB如下:
`timescale 1ns/1ns
module test_tb();
reg [3:0] data_4Q2; //有符号数,符号1位,整数2位,小数2位
wire [1:0] data_2Q0; //有符号数,符号1位,整数2位,小数0位
integer i; //循环变量
initial begin
data_4Q2 = 0; //输入赋初值
for(i=0;i<16;i=i+1)begin //遍历所有的输入,共16个
data_4Q2 = i;
#5;
$display("data_4Q2:%h data_2Q0:%h",data_4Q2,data_2Q0);
end
#20 $stop(); //结束仿真
end
//例化被测试模块
test test_inst(
.data_4Q2 (data_4Q2),
.data_2Q0 (data_2Q0)
);
endmodule
同时,我们也用matlab来实现同样的功能,观察两者的输出是否一致:
%--------------------------------------------------
% 关闭无关内容
clear;
close all;
clc;
%--------------------------------------------------
% 生成数据并做floor处理
x = -2:0.25:1.75;
F = fimath('RoundingMethod','Floor'); % 设定舍入模式为floor
data_4Q2 = fi(x,1,4,2,F); % 生成Q4.2格式的定点数
data_2Q0 = fi(data_4Q2,1,2,0,F); % 从Q4.2格式转换成Q2.0格式
% 打印数据
for i=1:length(data_4Q2)
fprintf('data_4Q2:%s data_2Q0:%s\n',hex(data_4Q2(i)),hex(data_2Q0(i)))
end
下图是2者分别输出的数据(16进制),可以看到是一致的,证明RTL代码无误。
定点数从Q4.2格式转Q2.0格式是一个比较特殊的例子,因为它相当于把小数部分全部舍弃掉了,如果舍入要求不是全部小数位,而是部分小数位,那么处理方式是一样的吗?
是一样的。对于其他情况则只需要把精度要求外的小数部分舍弃即可。例如Q5.3格式的定点数转Q3.1格式,则只需要把最后两位小数舍弃即可,例如:
00.111 是0.875,向下距离它最近的Q3.1格式的数是0.5即00.1,即00.111 >> 00.1
相当于舍弃掉精度外的小数部分即后两位小数,其他类似,不赘述了。