DSP浮点转定点方法总结要点.docx
《DSP浮点转定点方法总结要点.docx》由会员分享,可在线阅读,更多相关《DSP浮点转定点方法总结要点.docx(22页珍藏版)》请在冰豆网上搜索。
DSP浮点转定点方法总结要点
定点运算方法
1.1数的定标
对某些处理器而言,参与数值运算的数就是16位的整型数。
但在许多情况下,数学运算过程中的数不一定都是整数。
那么,如何处理小数的呢?
应该说,处理器本身无能为力。
那么是不是就不能处理各种小数呢?
当然不是。
这其中的关键就是由程序员来确定一个数的小数点处于16位中的哪一位。
这就是数的定标。
通过设定小数点在16位数中的不同位置,就可以表示不同大小和不同精度的小数了。
数的定标用Q表示法。
表1.1列出了一个16位数的16种Q表示能表示的十进制数值范围和近似的精度。
Q表示
精度(近似)
十进制数表示范围
Q15
0.00002
-1≤X≤0.9999695
Q14
0.00005
-2≤X≤1.9999390
Q13
0.0001
-4≤X≤3.9998779
Q12
0.0002
-8≤X≤7.9997559
Q11
0.0005
-16≤X≤15.9995117
Q10
0.001
-32≤X≤31.9990234
Q9
0.002
-64≤X≤63.9980469
Q8
0.005
-128≤X≤127.9960938
Q7
0.01
-256≤X≤255.9921875
Q6
0.02
-512≤X≤511.9804375
Q5
0.04
-1024≤X≤1023.96875
Q4
0.08
-2048≤X≤2047.9375
Q3
0.1
-4096≤X≤4095.875
Q2
0.25
-8192≤X≤8191.75
Q1
0.5
-16384≤X≤16383.5
Q0
1
-32768≤X≤32767
表1.1Q表示、S表示及数值范围
从表1.1可以看出,同样一个16位数,若小数点设定的位置不同,它所表示的数也就不同。
例如:
16进制数2000H=8192,用Q0表示
16进制数2000H=0.25,用Q15表示
从表1.1还可以看出,不同的Q所表示的数不仅范围不同,而且精度也不相同。
Q越大,数值范围越小,但精度越高;相反,Q越小,数值范围越大,但精度就越低。
例如,Q0的数值范围是-32768到+32767,其精度为1,而Q15的数值范围为-1到0.9999695,精度为1/32768=0.00003051。
因此,对定点数而言,数值范围与精度是一对矛盾,一个变量要想能够表示比较大的数值范围,必须以牺牲精度为代价;而想提高精度,则数的表示范围就相应地减小。
在实际的定点算法中,为了达到最佳的性能,必须充分考虑到这一点。
浮点数与定点数的转换关系可表示为:
浮点数(x)转换为定点数(
):
定点数(
)转换为浮点数(x):
例如,浮点数x=0.5,定标Q=15,则定点数
=
,式中
表示下取整。
反之,一个用Q=15表示的定点数16384,其浮点数为16384×2-15
=16384/32768=0.5。
1.2c语言:
从浮点到定点
下面所描述的几种基本运算是浮点到定点转换中经常遇到的,从中可以体会到一些基本的技巧和方法。
1.2.1加法
设浮点加法运算的表达式为:
floatx,y,z;
z=x+y;
将浮点加法/减法转化为定点加法/减法时最重要的一点就是必须保证两个操作数的定标值一样。
若两者不一样,则在做加法/减法运算前先进行小数点的调整。
为保证运算精度,需使Q值小的数调整为与另一个数的Q值一样大。
此外,在做加法/减法运算时,必须注意结果可能会超过16位表示,即数的动态范围。
如果加法/减法的结果超出16位的表示范围,则必须保留32位结果,以保证运算的精度。
1.结果不超过16位表示范围
设x的Q值为Qx,y的Q值为Qy,且Qx>Qy,加法/减法结果z的定标值为Qz,则
z=x+yÞ
=
=
Þ
一般情况,我们取x,y和z的定标值相同,即Qx=Qy=Qz=Qa。
所以定点加法可以描述为:
shortx,y,z;//Qa
z=add(x,y);//Qa
函数add()有防饱和机制,如果可以确信x+y不会溢出(-2^15<=z<=2^15-1),可以直接写为z=x+y.
定点减法:
shortx,y,z;//Qa
z=sub(x,y);//Qa
函数sub()有防饱和机制,如果可以确信x-y不会溢出(-2^15<=z<=2^15-1),可以直接写为z=x-y.
2.结果超过16位表示范围
设x的Q值为Qx,y的Q值为Qy,且Qx>Qy,加法结果z的定标值为Qz,则定点加法为:
intx,y;
longtemp,z;
temp=y<<(Qx-Qy);
temp=x+temp;
z=temp>>(Qx-Qz),若Qx≥Qz
z=temp<<(Qz-Qx),若Qx≤Qz
一般情况,我们取x,y和z的定标值相同,即Qx=Qy=Qz=Qa。
所以定点加法可以描述为:
intx,y,z;//Qa
z=L_add(x,y);//Qa
函数L_add()有防饱和机制,如果可以确信x+y不会溢出(-2^31<=z<=2^31-1),可以直接写为z=x+y.
定点减法:
intx,y,z;//Qa
z=L_sub(x,y);//Qa
函数L_sub()有防饱和机制,如果可以确信x-y不会溢出(-2^31<=z<=2^31-1),可以直接写为z=x-y.
3.结果超过32位表示范围
这种情况下位数超出了标准c语言的数的表示范围,只能用数组来保存变量。
定点加法可以描述为:
#defineNN_DIGITunsignedint
NN_DIGITx[digits],y[digits],z[digits];//Qa
NN_Add(z,x,y,digits);//Qa
定点减法:
NN_DIGITx[digits],y[digits],z[digits];//Qa
NN_Sub(z,x,y,digits);Qa
应注意的是以上32位定点加减法都是无符号数操作。
1.2.2乘法
1.16位乘法
设浮点乘法运算的表达式为:
floatx,y,z;
z=xy;
假设经过统计后x的定标值为Qx,y的定标值为Qy,乘积z的定标值为Qz,则
z=xyÞ
=
Þ
=
所以定点表示的乘法为:
shortx;//Qx
shorty;//Qy
intz;//Qz
z=L_mult(x,y)>>(Qx+Qy+1-Qz);
上式中x乘y的定标本来应该是Qx+Qy,但为了处理方便,函数L_mult()多乘了一次2,因此要再加1。
函数L_mult()有防饱和机制,如果可以确信z=xy不会溢出(-2^31<=z<=2^31-1),可以直接写为z=(xy)>>(Qx+Qy-Qz)。
2.结果超过32位表示范围
这种情况下位数超出了标准c语言的数的表示范围,只能用数组来保存变量。
定点乘法可表示为:
#defineNN_DIGITunsignedint
NN_DIGITx[digits];
NN_DIGITy[digits];
NN_DIGITz[2*digits];
NN_Mult(z,x,y,digits);
应注意的是以上32位乘法都是无符号数操作,如果需要做有符号数乘法,则需要根据乘数的符号来判断。
例1
设x=18.4,y=36.8,则浮点运算值为z=18.4×36.8=677.12;
设Qx=10,Qy=9,Qz=5,所以
intx=18841;//Q10
inty=18841;//Q9
z=L_mult(18841,18841)>>(10+9+1-5)=354983281L>>14=21666;
因为z的定标值为5,故定点z=21666即为浮点的z=21666/32=677.08。
例2
设x=18.4,y=36.8,则浮点运算值为z=18.4×36.8=677.12;
#defineNN_DIGITunsignedint
设Qx=20,Qy=20,Qy=20,所以
NN_DIGITx=18.4*(1<<20);//Q20
NN_DIGITy=36.8*(1<<20);//Q20
NN_DIGITz[2];//Q20
NN_Mult(z,&x,&y,1);//Q40
NN_Rshift(z,z,20,1);//Q(40-20)
1.2.3除法
1.32位除法
设浮点除法运算的表达式为:
floatx,y,z;
z=x/y;
假设经过统计后被除数x的定标值为Qx,除数y的定标值为Qy,商z的定标值为Qz,则
z=x/yÞ
=
Þ
所以定点表示的除法为:
intx,y,z;
z=L_shl(x,(Qz-Qx+Qy))/y;//Qz
2.32位以上的除法
这种情况下位数超出了标准c语言的数的表示范围,只能用数组来保存变量。
#defineNN_DIGITunsignedint
NN_DIGITx[2*digits];//Qx
NN_DIGITy[digits];//Qy
NN_DIGITz[digits];//Qz
NN_Lshift(x,x,(Qz-Qx+Qy),2);
NN_Div(z,x,2*digits,y,digits);
做以上运算是要保证Qz-Qx+Qy<32,否则要多次移位来实现;应注意的是以上除法都是无符号数操作,如果需要做有符号数除法,需要根据被除数和除数的符号来判断。
例1:
设x=18.4,y=36.8,浮点运算值为z=x/y=18.4/36.8=0.5;
根据上节,得Qx=10,Qy=9,Qz=15;所以有
intx=18841,y=18841;
z=L_shl(x,(15-10+9))/18841;//308690944L/18841=16384
因为商z的定标值为15,所以定点z=16384即为浮点z=16384/215=0.5。
1.2.4三角函数运算
1正弦和余弦
一般求cos、sin用查表法,方法是预先定义正弦和余弦表,表的长度及表中各元素的定标是根据精度要求确定的,精度要求越高,表的长度及元素的定标都可以增加。
余弦表制作步骤:
1)计算cos(2*pi*t/N),其中0<=t<=N-1,N是0~2*pi之间的采样点数。
2)将以上结果(浮点数)按精度要求定标,如Q15,
3)建立数组tab_cos[N],将以上结果作为该数组的元素。
正弦表的定义方法同上。
附录2中给出了余弦表tab_cos[360]和正弦表tab_sin[360],精度是Q15。
例1求cos(2*pi*x/32),x是定标为Qx的整数
cos(2*pi*x/32)=cos((2*pi*(360*x/32)/360);
程序如下:
intu=(L_mult(360,x)/32)%360;//Qx
intresult;
result=tab_cos[u];//Qx
例2求cos(x),x是定标为Qx的整数
cos(x)=cos(2*pi*(360*x/(2*pi))/360),
程序如下:
intpi_Qx=3.1415*(1<intu=(L_mult(360,x)/(2*pi_Qx))%360;//Q0
intresult;
result=tab_cos[u];//Qx
上式中将pi定标为Qx的定点数。
如何进一步提高精度
一般可以增加表的长度即采样点数来提高精度,但在现有采样情况下,也有办法来提高精度。
方法是求出两采样点之间的斜率,根据当前采样点的位置求出更加精确的值。
例3求cos(x),x是定标为Qx的整数
intpi_Qx=3.1415*(1<intu=(L_mult(360,x)/(2*pi_Qx))%360;//Q0
intpos=(L_mult(360,x))%(2*pi_Qx);
intresult;
result=tab_cos[u]+(tab_cos[(u+1)%360]–tab_cos[u])*pos/(2*pi_Qx);
上面(u+1)%360以防u=359时越界。
2反正切atan()
反正切函数
y=atan(x)
在x=-0.5~0.5之间近似线性,可以用y=x代替,在abs(x)>0.5时可以采用拟合的方法。
因此:
xabs(x)<0.5
y=atan(x)≈
(-0.06)*x.^2+0.5*x+0.30.5拟合可以调用matlab的命令ployfit来做,例如:
x=[start:
0.1:
stop];
y=atan(x);
pa=polyfit(x,y,2);
上式中的运算都是简单的乘法运算,较为简单。
1.2.5开方运算
浮点开方运算描述为:
floatx,y;
y=sqrt(x);
定点求开方有多种方法,各种方法在收敛速度上不尽相同,下面介绍几种常用的迭代算法。
1.Newton-Raphson-Babylonian算法:
给定整数N,求sqrt(N)。
首先确定初值x[0],然后利用一个简单的迭代公式:
x[n+1]=(x[n]+N/x[n])/2
迭代次数的选择:
迭代次数与初值x[0]的选取很有关系,x[0]越接近sqrt(N),收敛越快。
但总的来说,该方法收敛较快。
缺点是收敛时间不确定。
2.确定收敛速度的算法:
该方法描述如下:
intsqrt(intx)
{inttest,step;
if(x<0)return(-1);if(x==0)return(0);
step=1<<15;
test=0;
while(step!
=0)
{
registerinth;
h=(test+step)*(test+step);
if(h<=x){test+=step;}
if(h==x)break;
step>>=1;
}
return(test);}
以上例子是32位开放运算,32位以上的开方运算可参考附录1voidfixsqrt(UINT4*a,UINT4*b,intdigits),方法同上。
求开方还可以运用线性拟合的方法,由于曲线变化较快,必须根据自变量的范围分段拟合才能达到理想的精度。
1.3附录
1.3.1附录1:
定点函数库
/*___________________________________________________________________________
||
|FunctionName:
L_add|
||
|Purpose:
|
||
|32bitsadditionofthetwo32bitsvariables(L_var1+L_var2)with|
|overflowcontrolandsaturation;theresultissetat+2147483647when|
|overflowoccursorat-2147483648whenunderflowoccurs.|
||
|Complexityweight:
2|
||
|Inputs:
|
||
|L_var132bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var3<=0x7fffffff.|
||
|L_var232bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var3<=0x7fffffff.|
||
|Outputs:
|
||
|none|
||
|ReturnValue:
|
||
|L_var_out|
|32bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var_out<=0x7fffffff.|
|___________________________________________________________________________|
*/
Word32L_add(Word32L_var1,Word32L_var2)
/*___________________________________________________________________________
||
|FunctionName:
L_sub|
||
|Purpose:
|
||
|32bitssubtractionofthetwo32bitsvariables(L_var1-L_var2)with|
|overflowcontrolandsaturation;theresultissetat+214783647when|
|overflowoccursorat-214783648whenunderflowoccurs.|
||
|Complexityweight:
2|
||
|Inputs:
|
||
|L_var132bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var3<=0x7fffffff.|
||
|L_var232bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var3<=0x7fffffff.|
||
|Outputs:
|
||
|none|
||
|ReturnValue:
|
||
|L_var_out|
|32bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var_out<=0x7fffffff.|
|___________________________________________________________________________|
*/
Word32L_sub(Word32L_var1,Word32L_var2)
/*___________________________________________________________________________
||
|FunctionName:
add|
||
|Purpose:
|
||
|Performstheaddition(var1+var2)withoverflowcontrolandsaturation;|
|the16bitresultissetat+32767whenoverflowoccursorat-32768|
|whenunderflowoccurs.|
||
|Complexityweight:
1|
||
|Inputs:
|
||
|var1|
|16bitshortsignedinteger(Word16)whosevaluefallsinthe|
|range:
0xffff8000<=var1<=0x00007fff.|
||
|var2|
|16bitshortsignedinteger(Word16)whosevaluefallsinthe|
|range:
0xffff8000<=var1<=0x00007fff.|
||
|Outputs:
|
||
|none|
||
|ReturnValue:
|
||
|var_out|
|16bitshortsignedinteger(Word16)whosevaluefallsinthe|
|range:
0xffff8000<=var_out<=0x00007fff.|
|___________________________________________________________________________|
*/
Word16add(Word16var1,Word16var2)
/*___________________________________________________________________________
||
|FunctionName:
sature|
||
|Purpose:
|
||
|Limitthe32bitinputtotherangeofa16bitword.|
||
|Inputs:
|
||
|L_var1|
|32bitlongsignedinteger(Word32)whosevaluefallsinthe|
|range:
0x80000000<=L_var1<=0x7f