浮点型数据(float,double)存储IEEE标准解析和应⽤
在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.)这种⽆限循环,转换成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
3int main()
4 {
5float 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
18return0;
19 }
编译运⾏之后,得到f的值和他在内存中的地址。float几个字节多少位
打开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
3int main()
4 {
5double 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
18return0;
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的内存。
按回车键继续程序,发现修改成功。