Chinaunix首页 | 论坛 | 博客
  • 博客访问: 293893
  • 博文数量: 134
  • 博客积分: 667
  • 博客等级: 上士
  • 技术积分: 770
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-08 15:19
文章分类

全部博文(134)

文章存档

2012年(134)

分类:

2012-04-08 16:40:21

原文地址:详述浮点数的内存布局 作者:jmy2446267

先看下面一段程序:
  1. #include <stdio.h>

  2. int main(void)
  3. {
  4.     int a = 0x42cc0000;
  5.     float b = 102.0;
  6.     printf("%.2f\t%.2f\n", (float)a, *(float*)&a);
  7.     printf("%d\t%#x\n", (int)b, *(int*)&b);
  8.     return 0;
  9. }
运行结果如下:
1120665600.00    102.00
102              0x42cc0000
其中 1120665600 即为 0x42cc0000 的十进制形式。
 
下面将分析结果为什么会这样。
首先,用下面的宏判断系统是大端小端:
  1. #define little_endian() ((*(int *)"123" & 0xff) == '1')
由于我们主要是在x86 32位系统上,故int和float都占4字节。
且系统为小端,而所谓小端,简单地说,就是高位在高,低位在低。
我们对整型数已经很熟悉了,只是对浮点数还比较陌生,可能平时接触得不多的缘故吧,
接下来我们将好好了解浮点型数的内存布局。看如下代码:
  1. static inline int bit(char *s, int i)
  2. {
  3.     return (*(s + (i >> 3)) & 1 << (7 - (i & 7))) != 0;
  4. }

  5. static void show_bit(char *s, int from, int to)
  6. {
  7.     int i;
  8.     for (i = from; i <= to; i++)
  9.         printf("%d", bit(s, i));
  10.     printf(" ");
  11. }
关于以上 bit 函数还有一个小插曲,在 7 - (i & 7) 这个表达式中当没有这个括号时由于减号优先级高于 & ,逻辑上是不对的,然而我发现这不影响结果,大家可思考一下,为什么在这里即便没有这个括号,逻辑上错误,但是结果仍然是对的呢?
 
给定浮点数 b = 102.00, 我们调用函数 show_bit((char *)&b, 0, 31); 即可得到该浮点数在
内存中的位级表示,即为 0000 0000 0000 0000 1100 1100 0100 0010表示为十六进制即为 0x42cc0000。
 
由于是小端系统,所以我们将字节序列倒过来,如下
0100 0010 1100 1100 0000 0000 0000 0000
 
我们可将其分为三段,
 
第一段为第一个bit,表示符号,决定正负,这里为0,故为正数。
 
第二段为接下来的8bit,表示指数部分,在这里为1000 0101,十进制为133
浮点标准中规定,在这8bit非全0和非全1的情况下,指数的值为E = e - Bias
e即为这8bit代表的无符号数,Bias对于单精度浮点为127,因此在这里指数的值为
E = e - Bias = 133 - 127 = 6 补充一下,当这8bit全0时,指数值为E = 1 - Bias而非-Bias
 
第三段为剩余的23bit,表示小数域部分f,在这里是 1001 1000 0000 0000 000
即为0.10011,化成分数形式为19/32.
如果指数部分非全0和非全1,那么有效数M = 1 + f,否则M = f。
因此在这里有效数M = 1 + 19/32
综上,我们将该字节序列重新解读回float,即为
(1 + 19/32) * 64 = 102,其中64为2的6次方。
 
到这里,不知大家是否了解了呢?
写了如下代码,相信有助于大家了解浮点数的内存布局:
  1. void show_bit32(int i)
  2. {
  3.     char *s = (char *)&i;
  4.     show_bit(s, 0, 0);
  5.     show_bit(s, 1, 8);
  6.     show_bit(s, 9, 31);
  7.     printf("\n");
  8. }

  9. void show_float(float s)
  10. {
  11.     char *ptr = (char*)&s;
  12.     char buf[4] = {ptr[3], ptr[2], ptr[1], ptr[0]};
  13.     show_bit32(*(int*)buf);
  14. }

  15. char *input(void)
  16. {
  17.     static char buf[128];
  18.     printf(">>> ");
  19.     memset(buf, 0, sizeof buf);
  20.     fgets(buf, sizeof buf, stdin);
  21.     if (buf[strlen(buf) - 1] == '\n')
  22.         buf[strlen(buf) - 1] = '\0';
  23.     if (!*buf || strcmp(buf, "exit") == 0)
  24.         return NULL;
  25.     return buf;
  26. }

  27. int main(void)
  28. {
  29.     float f;
  30.     char *ptr;
  31.     while ((ptr = input()) != NULL) {
  32.         if (sscanf(ptr, "%f", &f) != 1)
  33.             continue;
  34.         show_float(f);
  35.     }
  36.     return 0;
  37. }
关于double型,原理大致相同,只是double为8字节,指数域为11位,小数域为52位而已,
如想深入了解相关机理,可研读《深入理解计算机系统》相关章节,这里就不赘述了。
 
阅读(1009) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~