在C语言中,float的内存布局非常奇怪,我们写一个简单的程序来测试:
int main(int argc, char* argv[]) {
float a = 1.0;
int b = 1;
return 0;
}
我们gdb调试打印
看到他的内存布局是0x3f8,特别奇怪。
而
float a = 1.5;
的内存布局是0x3fc。如下图:
特别奇怪。
float a = 4.0;
的内存布局又是0x408
下面解释一下float类型在内存中是怎么布局的。
浮点数在C/C++以及java中的内存布局是遵循IEEE标准的
简单解释一下,float类型是占用4个字节的,32个bit,第一位数符表示正负,1代表此浮点数是负数,0表示此浮点数事正数,接下来8位阶码表示指数位,范围为0-255,IEEE规定这个指数位减去127代表偏移,最后的23位表示小数部分,偏移为0的情况下,这23位数是这个浮点数的小数部分,比如
0x3f800000
二进制表示为
0011 1111 1000 0000 0000 0000 0000 0000
第一位是符号位0,表示正数
接下来8位 011 1111 1 是偏移位,换算成十进制是127。127-127=0位,偏移0位
接下来的是小数位:000 0000 0000 0000 0000 0000,小数位是0.
而按照规定,小数点前还有一个1,是不存储的,所以实际的小数位是1.0
当小数点大于0时,小数点向右偏移,反之,小数点向左偏移。
再看一个例子:
0x3fc00000
二进制表示为
0011 1111 1100 0000 0000 0000 0000 0000
第一位是符号位0,表示正数
接下来8位 011 1111 1 是偏移位,换算成十进制是127。127-127=0位,偏移0位
接下来的是小数位:100 0000 0000 0000 0000 0000,小数位是2^(-1)=0.5。
所以实际的小数位是1.5
可以看出小数位的计算方式,假如一个小数的计算方式比作11.1111......在计算时为:2^(1)+2^(0)+2^(-1)+2^(-2)+....
可以看出float的值是不连续的,事实上,浮点数以IEEE的标准所能精确表示的仅仅是其中的一部分。
例如0.3就不能被float精确表示,如下:
float a = 0.3;
内存中数据为:
是0x3e9999a
二进制表示为:
0011 1110 1001 1001 1001 1001 1010 0000
第一位是符号位0,表示正数
接下来8位 011 1110 1是偏移位,换算成十进制是125。125-127=-2位,向左偏移2位,是0.01
接下来的是小数位:001 1001 1001 1001 1010 0000,小数位是2^(-3)+2^(-4)+2^(-7)+2^(-8)+2^(-11)+2^(-12)+2^(-15)+2^(-16)+2^(-18)约等于0.3。