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/