FPGA量化/小数计算

定点数的量化

在FPGA开发时,对于浮点数,可以采用IEEE规定的浮点数格式,但那种浮点数运算的开销太大,对于小数位数比较少的浮点数,资源浪费又太多,可以采用定点数的形式来进行运算。

所谓定点数就是将小数点的位置固定,也就是说,整数部分和小数部分的位数固定,我们用整数来表示这个定点小数。

举个例子我们可以用(8,3)来表示8位二进制数据,其中最后的三位表示小数部分。

比如1.125,我们可以写成8'b00001001

因为小数部分是三位,相当于对于0~1 的小数,我们有\(2^3=8\)个刻度来表示,一个刻度的精度是\(1/8=0.125\),那么对于0.125我们可以写成001 ,对于整数部分1就可以写成二进制数00001 合起来就写成00001001 ,从另一方面,因为(8,3)的一个刻度的精度是0.125,1.125有9个0.125的刻度,所以写成\(9(d)=00001001(b)\)

对于1.125用(8,3)定点数可以表示为\(-2^4\times 0 + 2^3 \times 0 + 2^2 \times 0 + 2^1 \times 0 + 2^0 \times 1 + 2^{-1} \times 0 + 2^{-2} \times 0 + 2^{-3} \times 1\)对于最开始的负号,表示符号位

对于负数-0.125来说,可以先求0.125的定点数形式,即00000001,再求补码即11111111,如果展开来即为

\(-2^4\times 1 + 2^3 \times 1 + 2^2 \times 1 + 2^1 \times 1 + 2^0 \times 1 + 2^{-1} \times 1 + 2^{-2} \times 1 + 2^{-3} \times 1\)

那么对于随机一个数,比如1.128来说,因为(8,3)定点数的精度为0.125,所以会丢失一些精度,所以我们需要先对其进行量化,我们可以先将1.128乘上\(2^3\),按照上面定点数的展开形式可以看出,这时数字中已经没有小数部分(最后一位\(2^{-3}\)已经被消去),\(1.128 \times 8 = 9.024\),这时结果中的小数部分即为1.128中不能被量化的部分,我们对9.024取整即为我们对小数进行量化后的结果9,然后我们可以再对9处于\(2^{3}\),转换为小数形式\(9 / {2^3}=1.125\),再对其进行定点数的换算,当然可以直接用量化后的9乘上定点数的精度来进行换算。

定点数实际上就是精度数,而十进制小数可以写成
\(精度数 \times 定点数的精度\)
\(9 \times 0.125 = 1.125\)

定点数的运算

先以10进制为例。
如果我们能够计算12+34=46的话,当然也就能够计算1.2+3.4 或者 0.12+0.34了。所以定点小数的加减法和整数的相同,并且和小数点的位置无关。乘法就不同了。
12*34=408,而1.2*3.4=4.08。这里1.2的小数点在第1位之前,而4.08的小数点在第2位之前,小数点发生了移动。所以在做乘法的时候,需要对小数点的位置进行调整?!
可是既然我们是做定点小数运算,那就说小数点的位置不能动!!怎么解决这个矛盾呢,那就是舍弃最低位。 
也就说1.2*3.4=4.1,这样我们就得到正确的定点运算的结果了。所以在做定点小数运算的时候不仅需要牢记小数点的位置,还需要记住表达定点小数的有效位数。上面这个例子中,有效位数为2,小数点之后有一位。

对于定点小数的乘法运算,需要舍弃最低位,相应的就是向右进行移位,移位到我们规定的小数位数上(对于上面的例子要保证小数的位数为1位),向右移位等同于直接对数进行相除,我们也可以直接对乘法结果进行相除。

一个例子比较直观

对于2.1和2.2进行(13,12)的量化

\(2.1 *2^{12}=8601.6=8602\)

\(2.2 * 2^{12} = 9011.2 = 9011\)

两个定点数直接相乘

\(2.1 * 2^{12} * 2.2 * 2^{12}\)
与我们想要的结果\(2.1 * 2.2 * 2^{12}\)多乘上了一个\(2^{12}\),
所以需要将定点数直接相乘的结果除于\(2^{12}\)