浮点与定点

本文探讨了浮点数与定点数的概念、特点及其在计算机中的存储格式,讲解了浮点数转定点数的原理与实践技巧,适用于嵌入式系统开发人员。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:浮点与定点概述

1.1相关定义说明
       定点数:通俗的说,小数点固定的数。以人民币为例,我们日常经常说到的如123.45¥,789.34¥等等,默认的情况下,小数点后面有两位小数,即角,分。如果小数点在最高有效位的前面,则这样的数称为纯小数的定点数,如0.12345,0.78934等。如果小数点在最低有效位的后面,则这样的数称为纯整数的定点数,如12345,78934等。
       浮点数:一般说来,小数点不固定的数。比较容易的理解方式是,考虑以下我们日常见到的科学记数法,拿我们上面的数字举例,如123.45,可以写成以下几种形式:
12.345x101
1.2345 x102
0.12345 x103
……
为了表示一个数,小数点的位置可以变化,即小数点不固定。
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。大家可以很容易的从网络上下载到这篇文档。

下面,偶就大致的描述一下,感兴趣的“同志”们可以阅读原文。

--------------------------------------------------------

声明:
 
此文为原创,欢迎转载,转载请保留如下信息
 
作者:afreez 北京-中关村
 
联系方式:afreez.gan@gmail.com (欢迎与作者交流)
 
初次发布时间: 2006-12-19

初次发布在: http://blog.youkuaiyun.com/ganxingming/ 

不经本人同意,不得用语商业或赢利性质目的,否则,作者有权追究相关责任!
---------------------------------------------------------

 

       c语言中,单精度(float)数据类型为32bits,具体的如下图所示:

 

整个32bits分三部分,即

       Sign:符号位,1 bit0为正,1为负;

       Exponent(bias):指数部分,8 bits,存储格式为移码存储(后面还会说明),偏移量为127

       Mantissa(fraction):尾数部分。

 

       对应的双精度(double)类型的格式为:

同样,64位也被分为了三部分,对照单精度,不用我说就可以理解各个部分的含义了吧?

       是不是有点迷糊了,不要怕,理论这个东西最能忽悠人了,看起来很高深,其实也就是个屁大的事,举个例子就很容易明白了。

举例说明,如3.24x103,则对应的部分为,Sign03为指数部分(注意计算机里面存储的不是3,这里仅仅为了说明),3.24为尾数。我们知道,计算机“笨”的要死,只认识01,那么到底一个浮点数值在计算机存储介质中是如何存储的呢?

例如,我们要想偷窥浮点类型的值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类型的值,让你说出它是老几,知道怎么算了吧?如果不知道,看下面的图,我就不废话解释了。

22深入理解浮点存储格式

为了更深入的理解浮点数的格式。我们使用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;

 

好好琢磨琢磨吧,看明白了,就说明你基本明白了浮点数的存储格式,如果没有看明白,接着看,知道明白为止。(未完,待续......)

.定点数的加减乘除运算

简单的说,各种运算的原则就是先把待运算的数据放大一定的倍数,在运算的过程中使用的放大的数据,在最终需要输出结果的时候再调整回去。

 

举个例来说,有如下运算:

// coefs1 = 0.023423; coefs2=0.2131

float coefs1,coefs2;

int result;

result = 34* coefs1+72* coefs2;

代码的意思是,该模块需要输出一个整型的结果,但计算的过程中有浮点的运算。如果在定点的DSP中,这段代码是无法运行的。

为了解决这个问题,我们可以这样处理:首先,把coefs1coefs2等类似的浮点数据扩大一定的倍数(具体扩大多少倍,依据精度要求不同),我们暂且把小数点向右移动4位,也就是扩大的倍数为:*10000,在最终的输出的时候在缩小相同的倍数。修改后的代码大致如下:

// coefs1 = 234; coefs2= 2131

int coefs1,coefs2;

int result;

result = 34* coefs1+72* coefs2;

result /= 10000;

当然,上面的例子为了大家好理解,写的可能不是太正确,不过基本的精髓应该是这些了。具体的处理过程,大家可以在网上搜索“第3  DSP芯片的定点运算.doc”这篇文章,写的很具体,这里不再罗嗦了!

1)  除法转换为乘法或移位运算

我们知道,不管硬件平台如果变换,除法运算所需要的时钟周期都远远多于乘法运算和加减移位运算,尤其是在嵌入式应用中,“效率”显得尤为重要。以笔者的经验,其实,项目中的很大一部分除法运算是可以转换成乘法和移位运算,效率还是有很大提升空间的。

 

2)  查表计算

有些运算表达式可能牵扯到很多头疼的数学公式,尤其是在嵌入式硬件平台上,出现这种公式很是头疼,因为硬件相关的软件平台提供的功能很有限,有的就没有很多“常见”的开方等数学公式。如果该类运算在项目中很少出现,而且其取值的个数也不多,那么就可以考虑对各种情况加以分析,把各种可能的结果制作成一个静态的表格(可以理解成数组),再加以简单的条件判断语句就可以解决该类问题。

 

3)  级数展开

该问题的背景同上面的问题。对于一些数学公式,如果取值范围不好处理,就可以采用级数展开的方式。

 

4)  分子分母同时变化

对于一些除法运算,为了保证精度,如果分子的扩大范围不够大的话,可以考虑缩小分母,也可以达到预期效果。具体的例子可以参考我的另一篇文章“解决了个困扰了2天的问题,定点运算问题”。 

 

.举例及编程中的心得

51举例

“第3  DSP芯片的定点运算.doc”这篇文章中给了一个很简单有能说明问题的例子,不想动大脑了,直接引用过来如下。

这是一个对语音信号(0.3kHz~3.4kHz)进行低通滤波的C语言程序,低通滤波的截止频率为800Hz,滤波器采用19点的有限冲击响应FIR滤波。语音信号的采样频率为8kHz,每个语音样值按16位整型数存放在insp.dat文件中。

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

#include <stdio.h>

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<length;i++) x1[n+i-1]=xin[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[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<length;i++)  indata[i]=getw(fp1);        /*取一帧语音数据*/

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

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

}

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

return(0);

}

3.8  语音信号800Hz 19FIR低通滤波C语言定点程序

#include <stdio.h>

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-1]=xin[i];

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

{

sum=0;

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

xout[i]=sum>>15;

}

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

}

主程序与浮点的完全一样。

52编程中的心得

       通过上边的例子,相信大家都大致明白了转换的过程。一个函数模块如此,一个大的软件项目的转换也无非如此。所不同的是,对于一个项目,很少有一个个单独的模块,一般都是几个或数个模块都有联系,千一发而动全身,所以,再进行转换之前,最好理清各个软件模块之间的关系。

协调,协调,再协调。

 

写到这里, afreez一起学习DSP中浮点转定点运算这个问题的讨论终于可以收笔了。拖拖拉拉,拉了有3个月之久,现在终于可以了却自己的愧疚了。从第三部分开始,写的有写着急,因为没有足够的时间来写,还想把剩下的写完,只能这么解决了。我想,文章只是给出学者一个思路,如果自己以后遇到了这样的问题,还需要自己去探索的,没有一劳永逸的文章,毕竟问题都各个不同吗。

文章中难免有不妥之处,欢迎大家批评指正!

另外,自从发了该系列文章中的前两篇后,邮箱陆续收到一些朋友的email,我想,如果大家对这个问题感兴趣,可以多多的回帖,一方面,我回帖方便(不是太经常看油箱),一方面,也可以增加点这里的人气,哈哈。

四.定点数模拟浮点数运算及常见的策略

    相信大家到现在已经大致明白了浮点数转换成定点数运算的概貌。其实,原理讲起来很简单,真正应用到实际的项目中,可能会遇到各种各样的问题。具我的经验,常见的策略有如下几条:

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值