浮点型数据(float, double)存储IEEE标准解析和应用

时间:2024-02-24 18:31:08

  在C语言中,浮点型变量(也就是带小数位的实数)在内存中的存储方式遵循IEEE标准。

     首先来看单精度浮点型float。float占用4字节空间,也就是32位。从左向右数,第1位是符号位(0代表正数,1代表负数),接着是8位指数位,剩下的23位是数据位。如下所示

S EEEEEEEE DDDDDDDDDDDDDDDDDDDDDDD

    由于采用了科学计数法,所有的23位D位(数据位)全部用来记录小数点右边的数据,因为小数点左边只有1位且它肯定是1(二进制)。

    以3.5为例,它的二进制形式是 11.1,转换为科学计数法是 1.11E1。可以知道 S位(符号位 )应填入正(0),E位(指数位)为应填入1,D位(数据位)应填入11。

    PS:选择3.5的原因是它可以正好被精确地转换成二进制数。大多数的小数是不能被精确地转换的,这涉及到十进制小数转换二进制的精度问题。如3.6会是11.100110011001100(1100...)这种无限循环, 转换成IEEE标准的小尾则是 66 66 66 40。为了方便和精确,本文选择了3.5和-10.625这两个可以被精确转换的数。

    需要注意的是,E位的编码形式并非常用的补码形式(正数是它本身,负数符号位变1数据取反加一),而是把E位的8位能代表的数据空间(0-255)左右分为两半,以127为中点,代表0。如果指数为是1,则E位是128;如果指数位是2,则E位是129;如果指数位是-1,则E位是126,以此类推。

    回到3.5的例子,我们可以得到E位实际上应该是128,也就是10000000。D位从左向右开始填,没有的则为0。 所以,3.5的浮点表达为

0 10000000 11000000000000000000000

  整理为4位一组,则是

0100 0000 0110 0000 0000 0000 0000 0000

4      0       6      0       0      0       0      0

  可以推测,以小尾方式存储在内存中的3.5,它的形式应该是 0000 6040用如下的示例程序结合WinHex来验证。

 

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     float f = 3.5;
 6     printf("%f\r\n", f);
 7     printf("%p\r\n", &f);
 8     
 9     //此处用WinHex查看内存中f的存储是否和预想的一样
10     
11     system("pause");
12     
13     //此处用WinHex修改f在内存中的数值,并验证是否的到预期的新浮点数
14     printf("%f\r\n", f);
15     
16     system("pause");
17     
18     return 0;
19 }

 

  编译运行之后,得到f的值和他在内存中的地址。

    打开WinHex,选择“查看RAM”,找到此程序并查看primary memory

  通过“转到偏移地址”功能,跳到f的内存处(003BFAF8)。

    发现f的内存形式果然是刚才推测出的0000 6040

    验证完我们的推测之后,尝试通过直接修改内存的方式,把f的值由3.5改为-10.625。

    10.625的二进制形式为1010.101,科学计数法形式为1.010101E11。通过上面的公式,我们可以知道E位应填入130,即10000010。所以-10.625的浮点型是表达为

1 10000010 01010100000000000000000

1100 0001 0010 1010 0000 0000 0000 0000

C      1       2      A      0       0      0      0

  对应的小尾表达形式为0000 2AC1。

    通过WinHex修改f的内存。

    在程序中按回车键,程序继续运行,并输出f的值,此时可见f的值已被修改为-10.625。

  和float类型一样,double类型也可以表示小数形式的变量,区别是float占4字节,而double占8字节。在浮点数的编码上,double类型的E位有11位,多了3位(中值变为1023);D位有52位。

    在原程序的基础上修改,加入一个double变量d,把它用同样的步骤由3.5变为-10.625。

 

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5     double d = 3.5;
 6     printf("%f\r\n", d);
 7     printf("%p\r\n", &d);
 8     
 9     //此处用WinHex查看内存中f的存储是否和预想的一样
10     
11     system("pause");
12     
13     //此处用WinHex修改d在内存中的数值,并验证是否的到预期的新浮点数
14     printf("%f\r\n", d);
15     
16     system("pause");
17     
18     return 0;
19 }

   

    3.5(二进制11.1)的double形式为

0100 0000 0000 1100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

40 0C 00 00 00 00 00 00

  小尾方式为 00 00 00 00 00 00 0C 40

    编译程序后,利用WINHEX查看d的内存内容。

    果然是这个保存形式。

    同样的,我们尝试把d从3.5修改为-10.625(二进制-1010.101, -1.010101E11)。它的double表达形式为

1100 0000 0010 0101 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

C0 25 40 00 00 00 00 00

  小尾方式为00 00 00 00 00 40 25 C0。

    在WinHex中修改d的内存。

    按回车键继续程序,发现修改成功。