浮点运算转定点运算
与afreez⼀起学习DSP中浮点转定点运算
⼀:浮点与定点概述
1.1相关定义说明
定点数:通俗的说,⼩数点固定的数。以⼈民币为例,我们⽇常经常说到的如123.45¥,789.34¥等等,默认的情况下,⼩数点后⾯有两位⼩数,即⾓,分。如果⼩数点在最⾼有效位的前⾯,则这样的数称为纯⼩数的定点数,如0.12345,0.78934等。如果⼩数点在最低有效位的后⾯,则这样的数称为纯整数的定点数,如12345,78934等。
浮点数:⼀般说来,⼩数点不固定的数。⽐较容易的理解⽅式是,考虑以下我们⽇常见到的科学记数法,拿我们上⾯的数字举例,如123.45,可以写成以下⼏种形式:
12.345x101
1.2345 x102
0.12345 x103xi
……
为了表⽰⼀个数,⼩数点的位置可以变化,即⼩数点不固定。
1.2定点数与浮点数的对⽐
为了简单的把问题描述清楚,这⾥都是⼗进制数字举例,详细的分析,⼤家可以在后⾯的⽂章中看到。
(1)表⽰的精度与范围不同
例如,我们⽤4个⼗进制数来表达⼀个数字。对于定点数(这⾥以定点整数为例),我们表⽰区间[0000,9999]中的任何⼀个数字,但是如果我们要想表⽰类似1234.3的数值就⽆能为⼒了,因为此时的表⽰精度为1/100=1;如果采⽤浮点数来表⽰(以归整的科学记数法,即⼩数点前有⼀位有效位,为例),则可以表⽰[0.000,9.999]之间的任何⼀个数字,表⽰的精度为
1/103=0.001,精度⽐上⼀种⽅式提⾼了很多,但是表⽰的范围却⼩了很多。
也就是说,⼀般的,定点数表⽰的精度较低,但表⽰的数值范围较⼤;⽽浮点数恰恰相反。
(2)计算机中运算的效率不同
⼀般说来,定点数的运算在计算机中实现起来⽐较简单,效率较⾼;⽽浮点数的运算在计算机中实现起来⽐较复杂,效率相对较低。
(3)硬件依赖性
⼀般说来,只要有硬件提供运算部件,就会提供定点数运算的⽀持(不知道说的确切否,没有听说过不⽀持定点数运算的硬件),但不⼀定⽀持浮点数运算,如有的很多嵌⼊式开发板就不提供浮点运算的⽀持。
1.3与DSP的关系
⼀般说来,DSP处理器可以分为两⼤类:定点与浮点。两者相⽐较⽽⾔,定点DSP处理器速度快,功耗低,价格也便宜;⽽浮点DSP则计算精度⾼,动态范围⼤。
⼆:浮点数的存储格式
2.1 IEEE floating point standard
上⾯我们说了,浮点数的⼩数点是不固定的,如果每个⼈都按照⾃⼰的爱好存储在电脑⾥,那不就乱套了吗?那么怎么在计算机中存储这种类型的数字呢?象这类古⽼的问题前⼈早都为我们做好了相应
的规范,⽆规矩不成⽅圆吗。我们平时所说的浮点数的存储规范,就是由IEEE指定的,具体的规范⽂件是:IEEE Standard 754 for Binary Floating-Point Arithmetic。⼤家可以很容易的从⽹络上下载到这篇⽂档。
下⾯,偶就⼤致的描述⼀下,感兴趣的“同志”们可以阅读原⽂。
在c语⾔中,单精度(float)数据类型为32bits,具体的如下图所⽰:
整个32bits分三部分,即
Sign:符号位,1 bit,0为正,1为负;
Exponent(bias):指数部分,8 bits,存储格式为移码存储(后⾯还会说明),偏移量为127;
Mantissa(fraction):尾数部分。
对应的双精度(double)类型的格式为:
同样,64位也被分为了三部分,对照单精度,不⽤我说就可以理解各个部分的含义了吧?
是不是有点迷糊了,不要怕,理论这个东西最能忽悠⼈了,看起来很⾼深,其实也就是个屁⼤的事,
举个例⼦就很容易明⽩了。
举例说明,如3.24x103,则对应的部分为,Sign为0,3为指数部分(注意计算机⾥⾯存储的不是3,这⾥仅仅为了说
明),3.24为尾数。我们知道,计算机“笨”的要死,只认识0和1,那么到底⼀个浮点数值在计算机存储介质中是如何存储的呢?
例如,我们要想偷窥浮点类型的值4.25在计算机硬盘中存储的庐⼭真⾯⽬,请跟我来:⾸先把4.25转换成⼆进制的表达⽅式,即100.01,在详细点,变成1.0001x22,好了,对号⼊座把。
Sign=0;
Exponent(bias)=2+127=129 (偏移量为127,就是直接加上个127了);
Mantissa=1.0001-1.0=0001(规格化后,⼩数点前总是整数1,全世界⼈都知道前⾯是1不是0,所以省略不写了,即尾数部分不包括整数部分;当别⼈问你,为什么23 bit的尾数部分可以表⽰24位的精度,知道怎么回答了吧。靠,什么,没有看懂,再仔细读两便就知道了)。
对照上⾯的图⽰,相信你已经看明⽩了吧?相信你的智商。为了加深认识,再来⼀个。如果给定你⼀个⼆进制数字
串,01000000100010000000000000000000,并告诉你这是⼀个float 类型的值,让你说出它是⽼⼏,知道怎么算了吧?如果不知道,看下⾯的图,我就不废话解释了。
2.2深⼊理解浮点存储格式
为了更深⼊的理解浮点数的格式。我们使⽤C语⾔来做⼀件事。在C语⾔的世界⾥,强制类型转换,⼤家应该都很熟悉了。例如:
…
float f=4.6;
int i;
…
i = (int)(f+0.5); // i=5
..
下⾯我们不使⽤强制类型转化,我们⾃⼰来计算f转换成整形应该等于⼏?
把主要代码帖出来,如下:
//取23+1位的尾数部分
int ival= ((*(int *)(&fval)) & 0x07fffff) | 0x800000;
// 提取指数部分
int exponent = 150 - (((*(int *)(&fval)) >> 23) & 0xff);
if (exponent < 0)
ival = (ival<< -exponent);
else
ival = (ival >> exponent);
// 如果⼩于0,则将结果取反
if ((*(int *)&fval) & 0x80000000)
ival = -ival;
好好琢磨琢磨吧,看明⽩了,就说明你基本明⽩了浮点数的存储格式,如果没有看明⽩,接着看,知道明⽩为⽌。
3.定点数的加减乘除运算
简单的说,各种运算的原则就是先把待运算的数据放⼤⼀定的倍数,在运算的过程中使⽤的放⼤的数据,在最终需要输出结果的时候再调整回去。
举个例来说,有如下运算:
…
// coefs1 = 0.023423; coefs2=0.2131
float coefs1,coefs2;
int result;
…
result = 34* coefs1+72* coefs2;
…
代码的意思是,该模块需要输出⼀个整型的结果,但计算的过程中有浮点的运算。如果在定点的DSP中,这段代码是⽆法运⾏的。
为了解决这个问题,我们可以这样处理:⾸先,把coefs1,coefs2等类似的浮点数据扩⼤⼀定的倍数(具体扩⼤多少倍,依据精度要求不同),我们暂且把⼩数点向右移动4位,也就是扩⼤的倍数为:*10000,在最终的输出的时候在缩⼩相同的倍数。修改后的代码⼤致如下:
// coefs1 = 234; coefs2= 2131
int coefs1,coefs2;
int result;
…
result = 34* coefs1+72* coefs2;
result /= 10000;
…
当然,上⾯的例⼦为了⼤家好理解,写的可能不是太正确,不过基本的精髓应该是这些了。具体的处理过程,⼤家可以在⽹上搜索“第3章 DSP芯⽚的定点运算.doc”这篇⽂章,写的很具体,这⾥不再罗嗦了。
4.定点数模拟浮点数运算及常见的策略
相信⼤家到现在已经⼤致明⽩了浮点数转换成定点数运算的概貌。其实,原理讲起来很简单,真正应⽤到实际的项⽬中,可能会遇到各种各样的问题。具我的经验,常见的策略有如下⼏条:
1)除法转换为乘法或移位运算
我们知道,不管硬件平台如果变换,除法运算所需要的时钟周期都远远多于乘法运算和加减移位运算,尤其是在嵌⼊式应⽤中,“效率”显得尤为重要。以笔者的经验,其实,项⽬中的很⼤⼀部分除法运算是可以转换成乘法和移位运算,效率还是有很⼤提升空间的。
2)查表计算
有些运算表达式可能牵扯到很多头疼的数学公式,尤其是在嵌⼊式硬件平台上,出现这种公式很是头疼,因为硬件相关的软件平台提供的功能很有限,有的就没有很多“常见”的开⽅等数学公式。如果该类运算在项⽬中很少出现,⽽且其取值的个数也不多,那么就可以考虑对各种情况加以分析,把各种可能的结果制作成⼀个静态的表格(可以理解成数组),再加以简单的条件
判断语句就可以解决该类问题。
3)级数展开
该问题的背景同上⾯的问题。对于⼀些数学公式,如果取值范围不好处理,就可以采⽤级数展开的⽅式。
4)分⼦分母同时变化
对于⼀些除法运算,为了保证精度,如果分⼦的扩⼤范围不够⼤的话,可以考虑缩⼩分母,也可以达到预期效果。具体的例⼦可以参考我的另⼀篇⽂章“解决了个困扰了2天的问题,定点运算问题”。
分母变⼩了,相当于左移了
a=1-b/c;
|b|
|c|=pow(2,26)
|a|<1
在运算个过程中,需要把该运算转换成定点运算。
想了2天,都没有想出怎么可以保证a的精度,因为a可能很⼩,如果简单的
a<<=8;
是⽆法保证a的精度的。
今天快要下班时,突然有灵机⼀动:
c>>=6;
b<<=8;
a=1<<14-b/c; //相当于a<<14,保证了a的精度
分⼦不能左移太多位,分母右移,解决问题了。
发散思维说起来很容易,做起来不容易呀!
5.举例及编程中的⼼得
5.1举例
“第3章 DSP芯⽚的定点运算.doc”这篇⽂章中给了⼀个很简单有能说明问题的例⼦,不想动⼤脑了,直接引⽤过来如下。
这是⼀个对语⾳信号(0.3kHz~3.4kHz)进⾏低通滤波的C语⾔程序,低通滤波的截⽌频率为800Hz,滤波器采⽤19点的有限冲击响应FIR滤波。语⾳信号的采样频率为8kHz,每个语⾳样值按16位整型数存放在insp.dat⽂件中。
例3.7语⾳信号800Hz 19点FIR低通滤波C语⾔浮点程序
#include
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,0.01218354};
static int x1[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
{
sum=0.0;
for(j=0;j
xout[i]=(int)sum;
}
for(i=0;i<(n-1);i++) x1[n-i-2]=xin[length-1-i];
}
/
*主程序*/
void main( )
{
FILE *fp1,*fp2;
int frame,indata[length],outdata[length];
fp1=fopen(insp.dat,"rb"); /*输⼊语⾳⽂件*/
fp2=fopen(outsp.dat,"wb"); /*滤波后语⾳⽂件*/
frame=0;
while(feof(fp1)==0)
{
frame++;
printf("frame=%d\n",frame);
for(i=0;i
filter(indata,outdata,19,h); /*调⽤低通滤波⼦程序*/
for(i=0;i
}
支持小数点的进制转换器fcloseall( ); /*关闭⽂件*/
return(0);
}
例3.8语⾳信号800Hz 19点FIR低通滤波C语⾔定点程序
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论