底层硬件如何实现浮点运算
0赞首先,我们需要明确一个概念:FPGA所能表示的信号电平只有0和1。如表1所示,如果给出一组16位二进制数据,一般人肯定会很自然的认为它代表的是一组相应的10进制整数。
表1
16位二进制数据 |
10进制数据 |
16’b0000_0000_0000_0001 |
16’d1 |
16’b0000_0001_0000_0001 |
16’d257 |
16’b0100_0001_0000_1001 |
16’d16649 |
16’b1111_1111_1111_1111 |
16’65535 |
在不涉及浮点数之前(我们暂不讨论符号问题),的确就这么简单。但是现在我们换个角度来看看同样的一组16位二进制数所代表的浮点数值。
首先,我们要讨论定标的问题。如果定义了16位二进制数中的后8位代表小数,高8位依然代表整数,那么这个小数的最小单位是1/256(0.0039065),代表了这个浮点数的精度。同样的,表2列出了16位二进制数不同定标值的对应精度。
表2
定标(小数位数) |
精度值 |
4 |
1/16(0.0625) |
8 |
1/256(0.0039065) |
12 |
1/4096(0.000244140625) |
16 |
1/65536(0.0000152587890625) |
如表3所示,列出了定标为8的16位二进制数组所代表的浮点数。感兴趣的朋友也可以自己算一遍,加深理解。
表3
16位二进制数据 |
浮点数 |
16’b0000_0000_0000_0001 |
0.0039065 |
16’b0000_0001_0000_0001 |
1.0039065 |
16’b0100_0001_0000_1001 |
1023.0351565 |
16’b1111_1111_1111_1111 |
255.9960935 |
下面我们就要实现一组很有意思的运算:
R = 1.164(Y-16) + 1.596(Cr-128)
G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)
B = 1.164(Y-16) + 2.018(Cb-128)
这是图像处理中YCbCr转换为RGB的公式。我们不讨论它的具体涵义,我们只研究如何实现这样一组运算。R、G、B、Y、Cr、Cb这些变量都是8位无符号二进制整数,取值可以为0-255的任意值。
对上述公式中的出现的小数我们用一个12位数表示,我们注意到这些浮点数的整数部分最大值为2,那么我们只使用2位二进制数(可以表示0-3的整数,满足应用要求)来表示整数部分即可。用剩下的10位二进制数来表示小数部分。我们的思路是:先对这些浮点数左移10位(放大1024倍),然后代入公式运算,运算完成后对得到的RGB数据值右移10位(缩小1024倍)。
R*1024 = 1192*(Y-16) + 1634*(Cr-128)
= 1192*Y + 1634*Cr – 228224
= RR
G*1024 = 1192* (Y-16) – 400*(Cb-128) – 832*(Cr-128)
= 1192*Y – 400*Cb – 832*Cr + 138624
= GR
B*1024 = 1192* (Y-16) + 2066*(Cb-128)
= 1192*Y + 2066*Cb – 283520
= BR
这个运算也不是就此就结束了,由于在对浮点数的放大缩小过程中难免出现四舍五入的状况,因此也就带进了一定的误差。这个误差也许无关痛痒,但是在R、G、B计算的两个极端值(0或者255)附近就有可能出现类似溢出的问题。
所以,上一步运算完成得到的RR、GR、BR值需要做一些判断处理后再输出。Y、Cr、Cb位宽是8,浮点参数的位宽是12,那么RR、GR、BR值的位宽就取20(对于这个特定的公式,该位宽是绰绰有余的)。RR、GR、BR值右移10位后得到的RGB值是10位,而实际上使用低8位就足以表示实际值。但是此时有可能出现一些超出8位值的数据,这些数据就是放缩误差带来的牺牲品,我们对它的判断准则是:主要是针对RGB数值的最高2位取值,如果是00,则说明该值处于正常范围,直接输出低8位数据;如果是01,那么该值一定是正向溢出了,取低8位数据为255输出;如果是11,那么该值一定是负向溢出了,取低8位数据为0输出;该实例放缩1024倍,精度比较好,理论上不会出现最高两位是10的情况。
关于更多应用可以参考xilinx的IPcore中的一些运算相关的datasheet,《cordic.pdf》就是一份不错的资料。