特权同学

底层硬件如何实现浮点运算

0
阅读(1934)

首先,我们需要明确一个概念: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》就是一份不错的资料。

Baidu
map