浮点数在内存中的存储形式

来源:互联网 发布:严宽乔振宇 知乎 编辑:程序博客网 时间:2024/05/22 08:14

浮点数:
     浮点型变量在计算机内存中占用4字节(Byte),即32-bit。遵循IEEE-754格式标准。一个浮点数由2部分组成:底数m 和指数e。

    ±mantissa × 2exponent
(注意,公式中的mantissa 和 exponent使用二进制表示)
   底数部分 使用2进制数来表示此浮点数的实际值。
   指数部分 占用8-bit的二进制数,可表示数值范围为0-255。
   指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数。所以float的指数可从 -126到128
   底数部分实际是占用24-bit的一个值,由于其最高位始终为 1 ,所以最高位省去不存储,在存储中只有23-bit。
   到目前为止, 底数部分 23位加上指数部分 8位使用了31位。那么前面说过,float是占用4个字节即32-bit,那么还有一位是干嘛用的呢? 还有一位,其实就是4字节中的最高位,用来指示浮点数的正负,当最高位是1时,为负数,最高位是0时,为正数。

   浮点数据就是按下表的格式存储在4个字节中:

    Address+0 Address+1 Address+2 Address+3

   Contents SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
   S: 表示浮点数正负,1为负数,0为正数
   E: 指数加上127后的值的二进制数
   M: 24-bit的底数(只存储23-bit)

   注意:这里有个特例,浮点数为0时,指数和底数都为0,但此前的公式不成立。因为2的0次方为1,所以,0是个特例。当然,这个特例也不用认为去干扰,编译器会自动去识别。

举例1:计算机存储中的二进制数如何转换成实际浮点数
   通过上面的格式,我们下面举例看下-12.5在计算机中存储的具体数据:
   Address+0 Address+1 Address+2 Address+3

   Contents 0xC1 0x48 0x00 0x00
   接下来我们验证下上面的数据表示的到底是不是-12.5,从而也看下它的转换过程。

   由于浮点数不是以直接格式存储,他有几部分组成,所以要转换浮点数,首先要把各部分的值分离出来。

   Address+0 Address+1 Address+2 Address+3

   格式 SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM

   二进制 11000001 01001000 00000000 00000000

   16进制 C1 48 00 00

    可见:

    S: 为1,是个负数。

    E:为 10000010 转为10进制为130,130-127=3,即实际指数部分为3.

    M:为 10010000000000000000000。这里,在底数左边省略存储了一个1,使用实际底数表示为 1.10010000000000000000000

    到此,我们吧三个部分的值都拎出来了,现在,我们通过指数部分E的值来调整底数部分M的值。调整方法为:如果指数E为负数,底数的小数点向左移,如果指数E为正数,底数的小数点向右移。小数点移动的位数由指数E的绝对值决定。

    这里,E为正3,使用向右移3为即得: 1100.10000000000000000000 至次,这个结果就是12.5的二进制浮点数,将他换算成10进制数就看到12.5了,如何转换,看下面:

   小数点左边的1100 表示为 (1 × 23) + (1 × 22) + (0 × 21) + (0 × 20), 其结果为 12 。

   小数点右边的 .100… 表示为 (1 × 2-1) + (0 × 2-2) + (0 × 2-3) + ... ,其结果为.5 。

   以上二值的和为12.5, 由于S 为1,使用为负数,即-12.5 。

   所以,16进制 0XC1480000 是浮点数 -12.5 。

举例2:浮点数装换成计算机存储格式中的二进制数。
   举例将 17.625换算成 float型。

   首 先,将17.625换算成二进制位:10001.101 ( 0.625 = 0.5+0.125, 0.5即 1/2, 0.125即1/8 如果不会将小数部分转换成二进制,请参考其他书籍)
   再将 10001.101 向左移,直到小数点前只剩一位成了 1.0001101 x 2的4次方(因为左移了4位)。此时我们的底数M和指数E就出来了:
   底数部分M,因为小数点前必为1,所以IEEE规定只记录小数点后的就好,所以此处底数为 0001101 。

   指数部分E,实际为4,但须加上127,固为131,即二进制数 10000011 符号部分S,由于是正数,所以S为0.

   综上所述,17.625的 float 存储格式就是: 0 10000011 00011010000000000000000 转换成16进制:0x41 8D 00 00 所以,一看,还是占用了4个字节。

浮点数类型包括float、double、long double
在这里以float为例。
先看一段代码:
[cpp] view plain copy
  1. <span style="font-size:14px;">#include<stdio.h>  
  2. #include<windows.h>  
  3. int main()  
  4. {  
  5.     int a = 9;  
  6.     float *pa = (float *)&a;  
  7.     printf("a = %d\n", a);  
  8.     printf("*pa = %f\n", *pa);  
  9.     *pa = 9.0;  
  10.     printf("a = %d\n", a);  
  11.     printf("*pa = %f\n", *pa);  
  12.     system("pause");  
  13.     return 0;  
  14. }</span>  

它的运行结果如下:

为什么会有这样的结果呢?简单分析一下:
对于9和9.0在内存中肯定都以补码的形式存在,因为int和float对于这串补码的处理方式不同所以才会得到不一样的结果。
而对于float到底是怎样在内存中存储的呢?这就是我们讨论的重点!

根据国际标准IEEE(电气和电子工程协会)规定,任何一个浮点数NUM的二进制数可以写为:
NUM = (-1)^S*M*2^E;//(S表示符号,E表示阶乘,M表示有效数字)
①当S为0时,表示一个正数;当S为1时,表示一个负数
②M表示有效数字,1<= M <2
③2^E表示指数
比如十进制的3.0,二进制就是0011.0 就可以写成(-1)^0*1.1*2^1 
在比如十进制的-3.0,二进制就是-0011.0 就可以写成(-1)^1*1.1*2^1 
而规定float类型有一个符号位(S),有8个指数位(E),和23个有效数字位(M)
     double类型有一个符号位(S),有11个指数位(E),和52个有效数字位(M)
以float类型为例:

IEEE对于(有效数字)M和(指数)E有特殊的规定: (以float为例)
1.因为M的值一定是1<= M <2,所以它绝对可以写成1.xxxxxxx的形式,所以规定M在存储时舍去第一个1,只存储小数点之后的数字。这样做节省了空间,以float类型为例,就可以保存23位小数信息,加上舍去的1就可以用23位来表示24个有效的信息。
2.对于E(指数)E是一个无符号整数所以E的取值范围为(0~255),但是在计数中指数是可以为负的,所以规定在存入E时,在它原本的值上加上中间数(127),在使用时减去中间数(127),这样E的真正取值范围就成了(-127~128)。
对于E还分为三种情况:
①E不全为0,不全为1:
这时就用正常的计算规则,E的真实值就是E的字面值减去127(中间值),M的值要加上最前面的省去的1。
②E全为0
这时指数E等于1-127为真实值,M不在加上舍去的1,而是还原为0.xxxxxxxx小数。这样为了表示0,和一些很小的整数。
所以在进行浮点数与0的比较时,要注意。
③E全为1
当M全为0时,表示±无穷大(取决于符号位);当M不全为1时,表示这数不是一个数(NaN)

在看刚开始的题目:
①int a = 9;因为a是int类型的,所以他在内存中以补码的形式储存:
00000000 00000000 00000000 00001001
而*pa却是float类型的所以,当*pa读这块内存的值时,它通过浮点数的形式读取
0     0000000 0     0000000 00000000 00001001        
—   —————    ———————————————
S           E                                M
*pa = (-1)^0 * (0.0000000 00000000 00001001) * 2^(1-127)  这个数是一个很小的数,用十进制小数表示就是 0.000000

②*pa = 9.0;因为*pa是float类型的,所以9可以写为(1001)= (-1)^0 * (1.001) * 2^(3)
所以:
S = 0;M = 001000…… E = 3 + 127 = 130

0    1000001 0        0010000 00000000 00000000       
—   —————    ———————————————
S           E                                M

而把这个二进制数还原为十进制数就为1091567616就是a的值

原创粉丝点击