打印

C语言浮点数运算,讲述原理并总结了一些案例

[复制链接]
5388|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ifreecoding|  楼主 | 2012-5-22 19:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ifreecoding 于 2012-5-22 21:50 编辑

C语言浮点数运算.pdf (218.97 KB)
有些C语言书上说float型的有效位数是6~7位,为什么不是6位或者7位?而是一个变化的6~7位?
浮点数在内存中是如何存放的?
float浮点数要比同为4字节int定点数表示的范围大的多那么是否可以使用浮点数替代定点数?
为什么float型浮点数9.87654321 > 9.87654322不成立?为何10.2 - 9的结果不是1.2,而是1.1999998?为何987654321 + 987.654322的结果不是987655308.654322
如何才能精确比较浮点数真实的大小?
看完本文档,你将会得到答案!

在论坛上或QQ中有时会看到新同学问一些有关浮点数运算的问题,经常是走了错误的方向,但苦于交流方式不方便,无法为其详细说明,在此,我将我所掌握的一些知识加以整理写出来,希望对大家能有所帮助。更多案例请访问我的博客blog.sina.com.cn/ifreecoding
我主要是从事底层软件开发的,最开始写驱动程序,后来也做一些简单的业务层软件,在我所涉及的工作范围内,我使用的都是定点数,而且90%以上都是无符号定点数,在我印象中并没有使用过浮点数,即使做过一个专门使用DSP来处理信号的项目,也只是使用了无符号定点数,我将在另一篇案例《C语言使用定点数代替浮点数计算》里介绍定点数处理简单的浮点数的方法,这也是在底层驱动中常使用的方法

C语言标准C89里规定了3种浮点数,float、double和long double,其中float型占4个字节,double型占8个字节,long double型长度要大于等于double,本文档将以float型为例进行介绍,double和long double只是比float型位数长,原理都是一样的。
float型可以表示的范围是-3.402823466e38~3.402823466e38,而作为同为4个字节的定点数却只能表示-2147483648~2147483647的范围,使用同样的内存空间,浮点数却能比定点数表示大得多的范围,这是不是太神奇了?既然浮点数能表示这么大的范围,那么我们为何不使用浮点数来代替定点数呢?
先不说浮点数实现起来比较复杂,有些处理器还专门配置了硬件浮点运算单元用于浮点运算,主要原因是浮点数根本就无法取代定点数,因为精度问题。鱼和熊掌不可兼得,浮点数表示了非常大的范围,但它失去了非常准的精度。在说明精度问题前,我们先了解一下浮点数的格式。

ANSI/IEEE Std 754-1985标准IEEE 754是最广泛使用的二进制浮点数算术标准,被许多CPU与浮点运算器所采用。IEEE
754规定了多种表示浮点数值的方式,在本文档里只介绍32bitsfloat浮点类型。它被分为3个部分,分别是符号位Ssign bit)、指数偏差Eexponent bias)和小数部分Ffraction)。


S(1bit)  E(8bits)  F(23bits)


其中S位占1bit,为bit31S位为0代表浮点数是正数,S位为1代表浮点数是负数,比如说0x449A522CS位为0,表示这是一个正数,0x849A522CS位为1,表示这是一个负数。
E位占8bits,为bit23~bit30E位代表2N次方,但需要减去127,比如说E位为87,那么E位的值为287-127=9.094947017729282379150390625e-13。
F位占23bits,为bit0~bit22F位是小数点后面的位数,其中bit222-1=0.5bit212-2=0.25,以此类推,bit02-23=0.00000011920928955078125。但F位里隐藏了一个1,也就是说F位所表示的值是1+Fbit22~bit0所表示的数值),比如说F位是0b10100000000000000000001,只有bit22bit20bit01,那么F位的值为1+(2-1+2-3+2-23),为1.62500011920928955078125
综上所述,从二进制数换算到浮点数的公式为:(-1)2E-127×(1+F)。但还有几个特殊的情形:
u 若E位为0并且F位也为0时表示浮点数0,此时浮点数受S位影响,表现出+0-0两种0,但数值是相等的。比如二进制数0x00000000表示+0二进制数0x80000000表示-0
u 若E位为0并且F位不为0时浮点数为(-1)2-126×F注意,E位的指数是-126,而不是0-127=-127,而且F位是0.xx格式而不是1.xx格式,比如0x00000001的浮点数为2-126×2-23=1.4012984643248170709237295832899e-45而不是20-121×(1+2-23)。一旦E为不为0,从0变为1,不是增加2倍的关系,因为公式改变了。
u 若E位为255并且F位不为0时表示非数值,也就是说是非法数,例如0x7F800001
u 若E位为255并且F位为0时表示无穷大的数,此时浮点数受S位影响,例如0x7F800000表示正无穷大,0xFF800000表示负无穷大。当我们使用1个数除以0时,结果将被记作0x7F800000。

浮点型在多个处理器间通信时,传递的数值是它的二进制数,比如说1234.5678这个浮点数的二进制数是0x449A522B,如果使用串口发送的话,就会发现串口里发送的是0x440x9A0x520x2B4个数(发送的顺序也可能是逆序,这与约定的字节序有关,与浮点格式无关),接收端接收到这4个数字后再组合成0x449A522B,按照IEEE 754的定义被解析成1234.5678,这样就实现浮点数通信了。如果两个处理器所使用的浮点数规则不同,则无法传递浮点数。

浮点数的换算下面来看看浮点数与二进制数如何转换。

1,二进制数换算成浮点数:
假如在内存中有一个二进制数为0x449A522C,先将十六进制转换成二进制,如下:
0100  0100  1001  1010  0101  0010  0010  1100
按照SEF的格式分段,如下:
0  10001001  00110100101001000101100
这个数值不是特殊的情形,可以按照公式(-1)2E-127×(1+F)转换。S位的值为(-1)0=1E位的值为2137-127=1024F位的值为1+2-3+2-4+2-6+2-9+2-11+2-14+2-18+2-20+2-21= 1.205632686614990234375。最终结果为1×1024×1.205632686614990234375= 1234.56787109375
其中F位比较长,使用二进制方式转换比较麻烦,也可以先转换成十六进制再计算,转换为十六进制如下:
0011  0100  1010  0100  0101  1000
0x3   0x4   0xA   0x4   0x5   0x8
F位为23bits,需要在最后补一个0凑成24bits,共6十六进制数。F位的值为1+3×16-1+4×16-2+10×16-3+4×16-4+5×16-5+8×16-6=1.205632686614990234375,与上面使用二进制方法得到的结果相同。

2,浮点数换算成二进制数:
论坛有字数限制,剩下的部分请下载PDF文档看吧

相关帖子

沙发
wuhao1061| | 2013-10-16 15:26 | 只看该作者
顶一下

使用特权

评论回复
板凳
baiyunfei.k.f| | 2013-10-17 12:59 | 只看该作者
:victory:

使用特权

评论回复
地板
wg150986| | 2015-4-28 19:46 | 只看该作者

使用特权

评论回复
5
yanghooyak| | 2015-12-7 12:56 | 只看该作者
好强大呀楼主,谢谢

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

68

帖子

3

粉丝