浮点数的存储及输出问题

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

 

 

 

 

其实,上面的内容都是大学所学的东西,《体系结构》课程就讲这个。可惜这门课开在大四上学期,我一直忙于找工作,课都没有好好上,惭愧~~~

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值