浮点数据如何采用定点方式处理

  原文连接:http://www.eepw.com.cn/article/17893.htm

  许多芯片只支持整数运算,如果现在这些芯片上进行小数运算的话,定点小数运算应该是最佳选择了,此外即使芯片支持浮点数,定点小数运算也是最佳的速度选择。

        在世界中,由于芯片的限制,经常使用定点小数运算。所谓定点小数,实际上就是用整数来进行小数运算。下面先介绍定点小数的一些理论知识,然后以C语言为例,介绍一下定点小数运算的方法。在TI C5000 DSP系列中使用16比特为最小的储存单位,所以我们就用16比特的整数来进行定点小数运算。

        先从整数开始,16比特的储存单位最多可以表示0x0000到0xffff,65536种状态,如果它表示C语言中的无符号整数的话,就是从0到65535。如果需要表示负数的话,那么最高位就是符号位,而剩下的15位可以表示32768种状态。这里可以看出,对于计算机或者DSP芯片来说,符号并没有什么特殊的储存方式,其实是和数字一起储存的。为了使得无论是无符号数还是符号数,都可以使用同样的加法减法规则,符号数中的负数用正数的补码表示。

        我们都知道-1 + 1 =0,而0x0001表示1,那么-1用什么来表示才能使得-1 + 1 =0呢?答案很简单:0xffff。现在就可以打开Windows的计算器,用16进制计算一下0xffff+0x0001,结果是0x10000。那么0x10000和0x0000等价麽,我们刚才说过用16比特来表达整数,最高位的1是第17位,这一位是溢出位,在运算寄存器中没有储存这一位,所以结果是低16位,也就是0x0000。现在我们知道负数的表达方式了。举个例子:-100。首先我们需要知道100的16进制,用计算器转换一下,可以知道是0x0064,那么-100就是0x10000 - 0x0064,用计算器算一下得0xff9c。还有一种简单的转换符号的方法,就是取反加一:把数x写成二进制格式,每位0变1,1变0,最后把结果加1就是-x了。

        好,复习了整数的相关知识之后,我们进入定点小数运算环节。所谓定点小数,就是小数点的位置是固定的。我们是要用整数来表示定点小数,由于小数点的位置是固定的,所以就没有必要储存它(如果储存了小数点的位置,那就是浮点数了)。既然没有储存小数点的位置,那么计算机当然就不知道小数点的位置,所以这个小数点的位置是我们写程序的人自己需要牢记的。

         先以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,小数点之后有一位。

       现在进入二进制。我们的定点小数用16位二进制表达,最高位是符号位,那么有效位就是15位。小数点之后可以有0 - 15位。我们把小数点之后有n位叫做Qn,例如小数点之后有12位叫做Q12格式的定点小数,而Q0就是我们所说的整数。

       Q12的正数的最大值是 0 111 . 111111111111,第一个0是符号位,后面的数都是1,那么这个数是十进制的多少呢,很好运算,就是 0x7fff / 2^12 = 7.999755859375。对于Qn格式的定点小数的表达的数值就它的整数值除以2^n。在计算机中还是以整数来运算,我们把它想象成实际所表达的值的时候,进行这个运算。

       反过来把一个实际所要表达的值x转换Qn型的定点小数的时候,就是x*2^n了。例如 0.2的Q12型定点小数为:0.2*2^12 = 819.2,由于这个数要用整数储存, 所以是819 即 0x0333。因为舍弃了小数部分,所以0x0333不是精确的0.2,实际上它是819/2^12 =0.199951171875。

我们用数学表达式做一下总结:
x表示实际的数(*一个浮点数), q表示它的Qn型定点小数(一个整数)。
q = (int) (x * 2^n)
x = (float)q/2^n

由以上公式我们可以很快得出定点小数的+-*/算法:
假设q1,q2,q3表达的值分别为x1,x2,x3
q3 = q1 + q2   若 x3 = x1 + x2
q3 = q1 - q2   若 x3 = x1 - x2
q3 = q1 * q2 / 2^n若 x3 = x1 * x2
q3 = q1 * 2^n / q2若 x3 = x1 / x2
我们看到加减法和一般的整数运算相同,而乘除法的时候,为了使得结果的小数点位不移动,对数值进行了移动。
用c语言来写定点小数的乘法就是:
short q1,q2,q3;
....
q3=((long q1) * (long q2)) >> n;

由于/ 2^n和* 2^n可以简单的用移位来计算,所以定点小数的运算比浮点小数要快得多。下面我们用一个例子来验证一下上面的公式:
用Q12来计算2.1 * 2.2,先把2.1 2.2转换为Q12定点小数:
2.1 * 2^12 = 8601.6 = 8602
2.2 * 2^12 = 9011.2 = 9011
(8602 * 9011) >> 12 = 18923
18923的实际值是18923/2^12 = 4.619873046875 和实际的结果 4.62相差0.000126953125,对于一般的计算已经足够精确了。

  还有一篇文章也值得参考: https://www.jianshu.com/p/ef2211ca0e88

 

Q表示    S表示    十进制数表示范围

Q15    S0.15    -1≤x≤0.9999695

Q14    S1.14    -2≤x≤1.9999390

Q13    S2.13    -4≤x≤3.9998779

Q12    S3.12    -8≤x≤7.9997559

Q11    S4.11    -16≤x≤15.9995117

Q10    S5.10    -32≤x≤31.9990234

Q9     S6.9     -64≤x≤63.9980469

Q8     S7.8     -128≤x≤127.9960938

Q7     S8.7     -256≤x≤255.9921875

Q6     S9.6     -512≤x≤511.9804375

Q5     S10.5    -1024≤x≤1023.96875

Q4     S11.4    -2048≤x≤2047.9375

Q3     S12.3    -4096≤x≤4095.875

Q2     S13.2    -8192≤x≤8191.75

Q1     S14.1    -16384≤x≤16383.5

Q0     S15.0    -32768≤x≤32767

 

浮点至定点变换的C程序举例

本节我们通过一个例子来说明C程序从浮点变换至定点的方法。这是一个对语音信号(0.3~3.4kHz)进行低通滤波的C语言程序,低通滤波的截止频率为800Hz,滤波器采用19点的有限冲击响应FIR滤波。语音信号的采样频率为8kHz,每个语音样值按16位整型数存放在insp.dat文件中。

例1.7语音信号800Hz 19点FIR低通滤波C语言浮点程序。

#i nclude

const int length=180/*语音帧长为180点=22.5ms@8kHz采样*/

void filter(int xin[],int xout[],int n,float h[]);/*滤波子程序说明*/

/*19点滤波器系数*/

static float h[19]=

{0.01218354,-0.009012882,-0.02881839,-0.04743239,-0.04584568,

-0.008692503,0.06446265,0.1544655,0.2289794,0.257883,

0.2289794,0.1544655,0.06446265,-0.008692503,-0.04584568,

-0.04743239,-0.02881839,-0.009012882,O.01218354};

static int xl[length+20];

/*低通滤波浮点子程序*/

void filter(int xin[],int xout[],int n,float h[])

{

int i,j;

float sum;

for(i=0;i

for(i=0;i<length;i++)

{

sum=0.0;

for(j=0;j<n;j++)sum+=h[j]*x1[i-j+n-1];

xout=(int)sum;

for(i=0;i<(n-l);i++)x1[n-i-2]=xin[length-1-i];

}

/*主程序*/

void main()

FILE *fp1,*fp2;

int ,indata[length],outdata[length];

fp1=fopen(insp.dat,"rb");/* 输入语音文件*/

fp2=fopen(Outsp.dat,"wb");/* 滤波后语音文件*/

=0;

while(feof(fp1) ==0)

{

++;

printf(“=%d\n”,);

for(i=0;i<length;i++)indata=getw(fp1); /*取一帧语音数据*/

filter(indata,outdata,19,h);/*调用低通滤波子程序*/

for(i=0;i<length;i++)putw(outdata,fp2);/*将滤波后的样值写入文件*/

}

fcloseall();/*关闭文件*/

return(0);

}

例1.8语音信号800Hz l9点FIR低通滤波C语言定点程序。

#i nclude

const int length=180;

void filter (int xin[],int xout[],int n,int h[]);

static int h[19]={399,-296,-945,-1555,-1503,-285,2112,5061,7503,8450,

7503,5061,2112,-285,-1503,-1555,-945,-296,399};/*Q15*/

static int x1[length+20];

/*低通滤波定点子程序*/

void filter(int xin[],int xout[],int n,int h[])

int i,j;

long sum;

for(i=0;i<length;i++)x1[n+i-111=xin];

for(i=0;i<1ength;i++)

sum=0;

for(j=0;j<n;j++)sum+=(long)h[j]*x1[i-j+n-1];

xout=sum>>15;

for(i=0;i<(n-1);i++)x1[n-i-2]=xin[length-i-1];

}

主程序与浮点的完全一样

 

posted @ 2020-06-30 16:17  xiaokangkp  阅读(1083)  评论(0编辑  收藏  举报