目录
我们可以很快速的理解整型数在计算机中的存储模式,这是因为整型以二进制形式直接存储于内存中,而对于进制转换,是我们在学习计算机之初就已有所了解。
但是浮点数是一个困扰许多人的问题,因为其在内存中特殊的存储形式,使得不熟悉这种规则的人们无法理解,就比如下面的代码:
#include <stdio.h>
int main()
{
int x = 0x41440000;
float* fp = (float*)&x;
printf("&x = %08x ,x = %d \n", &x, x);
printf("fp = %08x ,*fp = %f \n", fp, *fp);
*fp = 10.25;
printf("&x = %08x ,x = %d \n", &x, x);
printf("fp = %08x ,*fp = %f \n", fp, *fp);
return 0;
}
输出的代码结果让我们产生困惑,为什么相同地址空间存储的数据,结果会出现那么大差距?
原来问题在于,int 和 float 类型的存储方式,我们所知道的,int 类型占4字节,其中最高一个bit位为符号位,其余31个bit位为数值位。
由图可以见得,整型的内存存储仅是将数值转变以二进制方式存储,但浮点数不同,接下来,我们来详细解读浮点数的内存存储规则
浮点数详细解读
1.小数的转换
小数向二进制转换的规则为:乘2取个位值,值是0,取0,值是1,取1,直到乘为0为止。
我们以12.25为例:取出小数部分,反复对其进行乘2操作,取结果的个位为当前小数位的值,再取结果的小数位重复以上操作,直到剩余的小数部分为0
2.浮点数的存储规则
根据国际标准IEEE754(电气和电子工程协会),任意一个二进制浮点数X可以表示成下面的形式 :
(-1) ^ S * M * 2 ^ E
- (-1)^S 表示符号位,当S = 0,X为正数; 当S = 1,X为负数。
- M表示有效数字,大于等于1,小于2。
- 2 ^ E 表示指数位。
例: (12.25)10→(1100.01)2; X的格式是: (-1) ^ 0 * 1.10001 * 2 ^ 3; S = 0,M = 1.10001; E = 3;
(-0.25)10→(-0.01)2 ; X的格式是: (-1) ^ 1 * 1 * 2 ^ -2; S = 1, M = 1; E = -2;
注意:IEEE754标准做了这样的规定:当尾数(小数)不为0时,尾数域的最高有效位为1,这称为浮点数的规格化。
例如 : (165.25)10→(1010 0101.01)2 -> (1.010010101 * 2^7);
规格化后的二进制小数,有了统一的规格,可以发现这样规格化之后,我们只需要存储一个尾数(即小数部分,整数部分恒为1)和指数部分。
1.IEEE754规定:对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。如图:
2.IEEE754规定:对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。如图:
- M规格化: frac被描述为小数值,且O≤frac<1,其二进制表示为0.frac。尾数定义为M=1+frac,则M=1.frac。那么就有1<M<2,由于总是能够调整阶码E,使得M在范围1≤M<2,所以不需要显示的表示它,这样还能获得一个额外的精度位。也就是说,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的frac部分,等到读取的时候,再把第一位的1加上去。
- M在规格化时,小数点可能左移,如:1100.01规格化为1.10001小数点左移3位,使1<=M<2;而0.001规格化为1小数点右移3位,使1<=M<2;
- E是8位二进制数(无符号数),表示范围是О~255;取中间位127;如果小数点左移n位,n为正值;如果小数点有移n位, n为负值;E= n + 127;
3.浮点数的范围
以float为例:
我们由前面的储存规则可以了解,浮点数的阶码E初始时保持中间值127,如果小数点左移n位,n为正值,如果小数点有移n位, n为负值,
E = n + 127,那么float的存储范围最大约为,最小约为
4.浮点数的精度
float的精度为6-7位,因为尾数M的位数为23,那么其可以存储精度为也就是6-7位
float的精度为15-16位,因为尾数M的位数为52,那么其可以存储精度为也就是15-16位
5.内存详解
回归刚开始的代码,为什么出现结果的差异,实际上是编译器对不同类型的解读方式不同
将x的值按照float来解读,可得符号位 0,阶码 10000010 可得 n = +3, 尾数 1.10001
可推出*fp = 1100.01,即也就是12.25