浮点数的存储及输出问题

 

Author Jeff       2005-11-25

 

 

 

 

 

 

 

 

 

关键字: 体系结构 IEEE754 浮点数 存储

 

 

 

 

 

 

 

 

 

 

 

 

 

 

main()
{
  float f=123.456;
  printf("f=%f/n",f);
}
如果不运行上面的代码,让我们来直接判断,输出的结果会是什么?

 

 

 

 

 

 

 

而在你运行程序之后,结果却很让人诧异:123.456001。为什么会是123.456001?有六位小数可以理解,最后那个1是为何?有很多人解释说最后那个1是乱码,随机的。嘿嘿~~其实无论你运行它多少次,最后始终都跟着一个1。这最后的那个1不是乱码,更不是随机的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

在数学中,表示一个浮点数需要三要素:尾数(Mantissa)、指数(Exponent,又称为阶码)和基数(Base)。任意一个浮点数N可以表示成下列形式:N = M × BE,例如N(10) =1.234×10-6 N(2)= -0.001011×2011等。

 

 

 

 

 

 

 

ME决定了浮点数的精度(precision),E指明小数点在B进制数据中的位置,因而EB决定了浮点数的表示范围(range),浮点数的符号(Sign)是单独考虑,表示为:

 

 

 

 

 

 

 

    符号位(S)

 

 

 

 

 

 

 

 

 

阶码(E)

 

 

 

 

 

 

 

 

 

尾数(M)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

为便于软件的移植,浮点数的表示格式应该有统一标准。1985IEEEInstitute of Electrical and Electronics Engineers)提出了IEEE754标准。该标准规定基数为2,阶码E用移码表示,尾数M用原码表示,根据原码的规格化方法,最高数字位总是1,该标准将这个1缺省存储,使得尾数的表示精度多了一位。实数的IEEE754标准的浮点数格式为:

 

 

 

 

 

 

 

类型

 

 

 

 

 

 

 

 

 

存储位数

 

 

 

 

 

 

 

 

 

总位数

 

 

 

 

 

 

 

 

 

偏移值
(offset)

 

 

 

 

 

 

 

 

 

数符(S)

 

 

 

 

 

 

 

 

 

阶码(E)

 

 

 

 

 

 

 

 

 

尾数(M)

 

 

 

 

 

 

 

 

 

短实数(float)

 

 

 

 

 

 

 

 

 

   1

 

 

 

 

 

 

 

 

 

   8

 

 

 

 

 

 

 

 

 

  23

 

 

 

 

 

 

 

 

 

   32

 

 

 

 

 

 

 

 

 

127

 

 

 

 

 

 

 

 

 

长实数(double)

 

 

 

 

 

 

 

 

 

   1

 

 

 

 

 

 

 

 

 

   11

 

 

 

 

 

 

 

 

 

  52

 

 

 

 

 

 

 

 

 

   64

 

 

 

 

 

 

 

 

 

 1023

 

 

 

 

 

 

 

 

 

说明:

 

 

 

 

 

 

 

   1) 约定小数点左边隐含有一位1,实际上使尾数的有效位数为24位,即尾数为1.M

 

 

 

 

 

 

 

   2) 偏移值 = 2 ^ (尾数位数 - 1) – 1。必须从指数中减去偏移值,才能确定有符号指数的实际值。

 

 

 

 

 

 

 

   3) 讨论float型:

 

 

 

 

 

 

 

      E = 0, M = 0 , S = 0, N = 0;  S = 1, N = -0-0可以表示一个很小的数,小到在单精度格式中不能

 

 

 

 

 

 

 

用数字和指数来表示。尽管如此,它们然小于0

 

 

 

 

 

 

 

      E = 0, M ≠ 0, 则数是有效的,但不是规格化数, N =  (-1) ^ S × 2 ^ ( - 127) × (0.M)

 

 

 

 

 

 

 

      E = 255, M = 0, 则数为正或负无穷大, 这取决于S.

 

 

 

 

 

 

 

      E = 255, M ≠ 0, N不是一个数,  表示为NaN.

 

 

 

 

 

 

 

      E = 1 ~ 254,  N = (-1) ^ S × 2 ^ (E - 127) × (1.M), 规格化数.

 

 

 

 

 

 

 

   4) 具体可以参见<编码的奥秘>一书。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 回到前面的问题,语句float f=123.456f; 经编译后生成的汇编语句为:

 

 

 

 

 

 

 

00401028   mov         dword ptr [ebp-4],42F6E979h

 

 

 

 

 

 

 

 十六进制值42F6E979h由何而来?

 

 

 

 

 

 

 

N (10) = 123.456,

 

 

 

 

 

 

 

换算成二进制表示:

 

 

 

 

 

 

 

N (2) = 1111011. 01110100101111001

 

 

 

 

 

 

 

= 1. 11101101110100101111001(...) * 2^6

 

 

 

 

 

 

 

   那么E – 127 = 6;  E = 127 + 6 = 133(10) = 10000101(2)

 

 

 

 

 

 

 

M = 111 0110 1110 1001 0111 1001 (省略了最高数字位1, 23bit)

 

 

 

 

 

 

 

   组合起来就是:

 

 

 

 

 

 

 

S          E                        M

 

 

 

 

 

 

 

0        10000101      111 0110 1110 1001 0111 1001

 

 

 

 

 

 

 

   4bit一间隔:

 

 

 

 

 

 

 

0100   0010   1111   0110   1110   1001   0111   1001

 

 

 

 

 

 

 

 4       2       F      6      E      9       7      9

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

再由42F6E979h还原为float

 

 

 

 

 

 

 

取出十六进制数42F6E979h, 化为二进制为
0*100 0010 1*111 0110 1110 1001 0111 1001(*
为区分点)
s=0, E=133, M=(1/2+1/4+1/8+1/32+1/64+1/256+1/512+...)=0.92900002002716064453125

N=1*64*(1+0.92900002002716064453125)=123.45600128173828125

哈哈...
大功告成,与输出的结果吻合.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

如果真要结果的话,可以这样:
printf("f=%0.3f/n",f);

 

 

 

 

 

 

 

以上环境为Window XP Professional + SP2,  Intel P4,  VC6.0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值