DSP

定点计算与浮点计算

2019-07-13 20:59发布

1.定点计算的必要性

        定点计算,简单来说,就是把小数转换为整数的一种计算。这个定点计算的点,就是小数点。从字面意思上说来,就是小数点固定的运算。在详细一点就是,对于整数来说,将假定的小数点固定在某个位置,将整数视为小数。一般来讲,小数点定在哪都无所谓的,决定了之后,那么这个小数点就固定在某一位了(所以叫做定点运算)。简单来说,就是把小数的计算,转换为整数的计算。那么,为什么要如此麻烦的转换过来计算呢?         ①对于实际的C语言程序,小数的表达常用[double]与[float]类型。换句话说,这个浮点数。对于实际信号处理,在处理前,我们必定会使用A/D去采集原本的模拟信号,然后将其转换为数字信号。这个数字信号,很显然,也不是小数。某些时候为了计算,我们甚至还得将其转换为浮点数,然后才能计算。         ②DSP分为定点DSP与浮点DSP。浮点DSP就是为浮点运算而量身定做的,其内部有硬件可以直接完成浮点运算。但是,由于其较为复杂的结构,计算耗时也很大以及价格等等的因素,其运用也有一定的限制。         ③限制结果的位数。         综上所述,为了计算的快速,掌握定点计算还是必须的。

2.负数的表达与三大规则

        ①规则一:最高位(MAB)是符号位。使用最高位作为符号位,下面我们使用4-bit的数来进行说明。最高位是符号位,符号位是1的话,其表示的是负数,反之则是正数。那么,此时,最高位的权重则是-8(4-bit),此时+6与-6的表示如下。
很显然,+6 = 4+2,-6 = -8+2成立。这样理解比较直观吧。还有就是,-6就是6的补码,这样理解也对。         ②规则二:负数的符号位扩展不影响数的大小。还是拿-6来说,在符6的前面无论补多少个1,这个数同样表示的是-6。当然,最高位是符号位(规则一)。

       ③规则三:一个数与符号位相乘,结果等于这个数的补码。当然,这是理所应当的事情。

3.固定小数点的折算与其实现(C语言)

       首先,陈述一个事实。一个4-bit的数乘以一个4-bit的数,其结果为8-bit。继续乘下去的话,位数迟早不够用的。        为了解决这个问题,我们可以引入小数点。假设0.9x0.9其结果为0.81。前两个乘数与被乘数的有效位数是1,那么结果只需要0.8就足够了。按照这个思路,只要使用绝对值为1一下的数来进行乘法,就可以有效的避免位数不够用了。为此,我们可以提出固定小数点。固定小数点对于DSP来讲,是不存在的,只是我们想象中的一个小数点。对此,对于一个4-bit的数,有以下几种小数点的取法。

按照之前我们讲的,要将数字压缩在绝对值小于1的范围内的话,我们可以选择小数点在②的位置。小数点在②的时候,其小数点后有3位,故称为Q3格式。很显然,Q3结构下,可以表示某个数X的范围是           这个范围。         根据自己选定的小数点位置与相应的权重,就可以进行折算。这里有个我写的Q15格式的折算的C程序。 unsigned short To_Q15_Fixed_Point(double Data_Float) { unsigned short Data_Q15_Format = 0; if(Data_Float == 1) return (unsigned short)0x7FFF; else if(Data_Float == -1) return (unsigned short)0x8000; else if(Data_Float < 0) { Data_Float = -Data_Float; Data_Q15_Format |= (unsigned short)0x8000; } Data_Q15_Format |= (short)(Data_Float*0x8000); if(Data_Q15_Format&0x8000) { Data_Q15_Format &= 0x7FFF; Data_Q15_Format = ((~Data_Q15_Format) + 0x0001); } return (unsigned short)Data_Q15_Format; }

4.固定小数点的乘法与其实现(C语言)

          固定小数点的乘法可以通过如下的方法去实现。首先,还是要说明一下,两个Q3格式的数相乘,其结果为Q6格式。
        有几个需要说明的地方:         ①在进行乘法计算时候,标志位代表的是负数,标志位乘以乘数,其结果为乘数的补码。         ②由于乘法这里是分布进行的,每一步骤的结果均为8-bit。根据规则二,若结果的标志位为1,则标志位之前的数字补1。若为0,则正常补充0就可以。         ③结果为Q6的一个数字,我们只需要按照Q3的要求,取出就可以了。由于是负数,第一位有没有无所谓。(注意,这里还有可能产生溢出,比如,两个负数相乘。这个时候,无视溢出,依旧按照Q3格式取结果,就可以了。)
       同样的,这里有Q15格式的乘法计算函数unsigned short MUL_Q15(unsigned short a, unsigned short b) { int Count; unsigned int Answer_Q30 = 0; unsigned short Answer_Q15 = 0; if(b&0x8000) { Answer_Q30 += (~((unsigned int)a) + 0x0001) <<15; b = (b & 0x7FFF); //printf("%8X+*",Answer_Q30); } for(Count=0;Count<=15;Count++) { if(b&(0x0001<> 15); return (unsigned short)Answer_Q15; }

4.固定小数点实际的运用

       我们看看固定小数点的实际运用吧。假设有如下FIR滤波器,其滤波器系数为 输入信号为
求其输出。        计算的思路是,将滤波器系数与h(n)转为Q15结构,然后进行整数计算,输出的结果为Q15。然后再转为小数就可以了。其代码如下。
#include #include #include #include double h[] = {0.25, 0.5, -0.25, -0.25, 0.5,0.25}; unsigned short MUL_Q15(unsigned short a, unsigned short b) { int Count; unsigned int Answer_Q30 = 0; unsigned short Answer_Q15 = 0; if(b&0x8000) { Answer_Q30 += (~((unsigned int)a) + 0x0001) <<15; b = (b & 0x7FFF); //printf("%8X+*",Answer_Q30); } for(Count=0;Count<=15;Count++) { if(b&(0x0001<> 15); //printf(" = %4X ",Answer_Q15); return (unsigned short)Answer_Q15; } unsigned short To_Q15_Fixed_Point(double Data_Float) { unsigned short Data_Q15_Format = 0; if(Data_Float == 1) return (unsigned short)0x7FFF; else if(Data_Float == -1) return (unsigned short)0x8000; else if(Data_Float < 0) { Data_Float = -Data_Float; Data_Q15_Format |= (unsigned short)0x8000; } Data_Q15_Format |= (short)(Data_Float*0x8000); if(Data_Q15_Format&0x8000) { Data_Q15_Format &= 0x7FFF; Data_Q15_Format = ((~Data_Q15_Format) + 0x0001); } return (unsigned short)Data_Q15_Format; } double To_Float(unsigned short Data_Q15) { if(Data_Q15 == 0x8000) return (double)(-1); if(Data_Q15&0x8000) { Data_Q15 = ~(Data_Q15 - 0x0001); return (double)(-(Data_Q15 / (double)0x8000)); } else return (double)(Data_Q15 / (double)0x8000); } unsigned short FIR_Filter_Q15(unsigned short *b_Q15, int b_Q15_Lenth, unsigned short *Input_Buffer_Q15, unsigned short Input_data_Q15) { int Count; double Output_Data_Q15 = 0; for(Count = 0 ; Count < b_Q15_Lenth - 1 ; Count++) { *(Input_Buffer_Q15 + Count) = *(Input_Buffer_Q15 + Count + 1); } *(Input_Buffer_Q15 + b_Q15_Lenth - 1) = Input_data_Q15; Input_Buffer_Q15 += b_Q15_Lenth - 1; for(Count = 0; Count < b_Q15_Lenth ;Count++) { //Output_Data_Q15 += (unsigned short)((*(b_Q15 + Count)) * // (*(Input_Buffer_Q15 - Count))>>15); Output_Data_Q15 += MUL_Q15(*(b_Q15 + Count), *(Input_Buffer_Q15 - Count)); //printf("%4X+",(unsigned short)MUL_Q15(*(b_Q15 + Count), // *(Input_Buffer_Q15 - Count))); //printf(" = "); } return (unsigned short)Output_Data_Q15; } int main(void) { /* MUL_Q15(0x4000, 0xE000);*/ unsigned short *h_Q15; h_Q15 = (unsigned short *) malloc(sizeof(unsigned short)*(sizeof(h)/sizeof(double))); memset(h_Q15, 0, sizeof(unsigned short)*(sizeof(h)/sizeof(double))); //-----------------------------display h--------------------------------// int Count; printf(" h : "); for (Count = 0;Count < (sizeof(h)/sizeof(double));Count++) { printf("% lf ", h[Count]); } printf(" "); printf("h_Q15 : "); for (Count = 0;Count < (sizeof(h)/sizeof(double));Count++) { *(h_Q15+Count) = To_Q15_Fixed_Point(h[Count]); printf("% 9X ",*(h_Q15+Count)); } printf(" -------------------------------------------------- "); //---------------------------------------------------------------------// double Input = 0 ; double Output = 0 ; unsigned short Input_Q15 = 0 ; unsigned short Output_Q15 = 0 ; unsigned short *Input_Buffer_Q15; Input_Buffer_Q15 = (unsigned short *) malloc(sizeof(unsigned short)*(sizeof(h)/sizeof(double))); memset(Input_Buffer_Q15, 0, sizeof(unsigned short)*(sizeof(h)/sizeof(double))); FILE* Input_Pointer; Input_Pointer = fopen("input.dat","r"); while(1) { if(fscanf(Input_Pointer, "%lf", &Input) == EOF) break; printf("% lf --> ", Input); Input_Q15 = To_Q15_Fixed_Point(Input); printf("%4X | ",(unsigned short)Input_Q15); //Input = To_Float(Input_Q15); //printf("% lf ",Input); Output_Q15 = FIR_Filter_Q15(h_Q15, (sizeof(h)/sizeof(double)), Input_Buffer_Q15, Input_Q15); printf("%4X --> ", Output_Q15); Output = To_Float(Output_Q15); printf("% lf ", Output); printf(" "); } printf("Finish " ); return (int)0; }        以上程序GCC下编译通过,执行的结果为
其计算后,输出的结果与Matlab一致。
       博客地址:http://blog.csdn.net/thnh169/