在许多情况下, 数学运算过程中的数不一定都是整数,数值运算也可能会包含整数运算
和小数运算。
小数的表示分为定点数和浮点数。在 FPGA 中,数字和信号变量等都是用二进
制表示的,整数部分和小数部分利用二进制小数点分开。
定点数的小数点位置固定不变,存储是小数点不进行存储,按照约定的位置计算数值。
浮点数的小数点位置不固定,可以用一个指数(E)和一个尾数(M)来表示,E决定了它的动态范围,M决定它的运算精度。
优点:提高动态范围和运算精度
缺点:处理速度慢
总结:FPGA开发中通常使用定点数运算,浮点数只在非实时运算具有广泛应用。
当一个定点数的小数点位置确定,它的动态范围和运算精度就确定了,确定小数点的位置即数的定标。
例如:
32Q16:表示一个有符号数,总位宽32分别为1个符号位+15个整数位+16小数位
总结:若定义一个数据为a(m>n),则
①mQn是一个有符号数
②总位宽为m
③小数位宽为n
有符号数与无符号数相比,它的最高位用来作为符号位,0表示正数,1表示负数。
考虑一个 4 位的整数 4’b1011.
如果它是一个无符号数据,那么它表示的值为:1*(23) +0*(22)+1*(21)+1*(20) = 11。
如果它是一个有符号数,那么它表示的值为:1*(-23)+ 0*(22)+ 1*(21)+1*(20) = -5。
例如:
8’b1001_1100
无符号数:156 ______ 有符号数:-100
8’b0110_1001
有符号数:105 ______ 有符号数:105
定点数的三种表示方式
68:
①原码:100_0100 (默认为0100_0100)
②反码:100_0100 (默认为0100_0100)
③补码:100_0100 (默认为0100_0100)
-68:
①原码(符号位+正数原码):1100_0100
②反码(符号位不变,其他位取反):1011_1011
③补码(反码+1):1011_1100
总结:
①正数的原、反、补码相同
②反码和补码是用来表示负数的
③负数补码形式可以通过基数和权重的乘积相加得到数值
例如:
4’b0101,由于最高位为 0,所以它是一个正数,如果要把它扩展成 6 位,那么只需要在最前面加 2 个 0 即可,扩展之后的结果为:6’b00_0101。
4’b1011,由于最高位为 1,所以它是一个负数,如果要把它扩展成 6 位,前面不是添 2 个 0,而是添 2 个 1,扩展之后的结果为:6’b11_1011。
验算:
4’b1011 = 1*(-23)+0*(22)+1*(21)+1*(20) = -8 + 0 + 2 + 1 = -5
6’b111011 = 1*(-25) + 1*(24)+1*(23)+0*(22)+1*(21)+1*(20) = -
32+16+8+2+1=-5
假设一个有符号小数为 4’b1011,它的数据格式为 4Q2,那么看看这个数表示的 10 进制数是多少
4’b10.11 = 1*(-21)+0*(20)+1*(2-1)+1*(2-2) = -2 + 0 + 0.5 + 0.25 = -1.25
一般情况,mQn 格式数据的数据范围为-2(m-n-1)~2(m-n-1)-1/2n。
现在要把这个数据用 6Q3 格式的数据存储。显然需要把整数部分和小数部分
分别扩一位,整数部分采用上一节提到的符号位扩展,小数部分则在最后面添一个 0,扩展
以后的结果为 6’b110110,接下来仍然做一个验证:
6’b110.110 = 1*(-22)+1*(21)+0*(20)+1*(2-1)+1*(2-2) +0*(2-3)= -4 + 2 + 0 +
0.5 + 0.25 + 0 = -1.25
两个有符号数相加,为了保证和不溢出,首先应该把两个数据进行扩展使小数点对齐,
然后把扩展后的数据继续进行一位的符号位扩展,这样相加的结果才能保证不溢出。
例如:
现在要把 5Q2 的数据 5’b100.01 和 4Q3 的数据 4’b1.011 相加。
①把 5Q2的数据 5’b100.01 扩位为 6Q3 的数据 6’b100.010,使它和 4Q3 数据的小数点对齐
②把 4Q3 的数据 4’b1.011 进行符号位扩展成 6Q3 的数据6’b111.011
为了数据不溢出他们的和应该由7Q3的数据来存储,所以再把他们分别扩展一个符号位即7Q3。
根据上文的mQn的数据范围:
6Q3:-4~4-1/23
两个6Q3:-8~8-1/22
7Q3:-8~8-1/23
mQn 和 aQb 数据相乘,积应该用(m+a)Q(n+b)格式的数据进行存储
假设一个 9Q6 格式的数据为:9’b011.101101,现在只想保留 3 位小数位,显然必
须把最后三位小数位截掉,但是不能直接把数据截成 6’b011.101,这样是不精确的,工程
上 一 般 也 不 允 许 这 么 做 。
正 确 的 做 法:
①:是 先 看 这 个 数 据 是 正 数 还 是 负 数(此例中最高位为0,是正数)
②:再看截掉部分的最高位是 0 还是 1(此例中截掉部分是最末尾的 101)
在数据是正数的情况下,如果截掉部分的最高位为1,那么是需要产生进位的(9’b011.101101 应该被截成 6’b011.110)
假设一个 9Q6 格式的数据为:9’b100.101101,由于最高位是 1,所以这个数是一个负数
再看截断部分的最高位以及除最高位的其他位是否有1,此例中截断部分(截断部分为末尾的 101)的最高位为 1,而且除最高位以外的其他位也有为 1 的情况,由于负数最高位的权重是(-2^2),所以对于这种情况是不需要进位的,与正数不同的是,负数不进位是需要加 1 的。因此最终 9’b100.101101 应该被截成6’b100.110。
负数的例子:
①10’b100.1011010
截位结果:100.110
截位最高位为1,其他位也有1
②10’b100.1011000
截位结果:100.101
截位最高位为1,其他位没有1
③10’b100.1010011
截位结果:100.101
截位最高位为0,其他位有1
所谓饱和处理就是如果计算结果超出了要求的数据格式能存储的数据的最大值,那么就
用最大值去表示这个数据,如果计算结果超出了要求的数据格式能存储的数据的最最小值,
那么就用最小值去表示这个数据。
首先定义位宽,例如16Q12,它的数据范围(-8~8-1/212),它的数据范围也可以表示为:
[(-2m-1~2m-1-1)/2n]即。(( -32768 ~ 32767)/212)
它可以表示的最小正数为寄存器最低位上的 1,对应:2-12,这个值称为量化精度。
①2.819
16Q12:2.819/2-12=11546.624 约等于 11547
②3.1415926
16Q12:12867.962 约等于 12868
2.819*3.1415926 = 8.8561495394
16Q12量化后相乘:
11547*12868 = 148,586,796 = 8.8564631938934326171875
误差为:0.000313654493