0%

浮点型数据在内存中的存储

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
int n=9;
float *pFloat =(float*)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);

*pFloat=9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}

思考下这段代码输出的值是什么?

可能你会感到好奇:

我明明&n后强转成了float类型,为什么还会输出0.000000呢?

为什么* pFloat=9.0,以%d的形式输出会得到这么大的一个数呢?

别急,我慢慢解释。

首先我们需要知道浮点型的数据在内存中是如何存储的。

浮点型的数据是根据国际标准“IEEE 754”来存储的,标准规定:每个浮点数V=(-1)^S* M*2^E

当S=0时,浮点数为正数

当S=1时,浮点数为负数

那么M与E又代表什么意思呢?

假设我们有一个浮点数“5.5”,把它转换成二进制的形式就是:101.1,此时它的S=0,M=1.01,E=2。

(就好比科学计数法,5500=5.5*10^3,只不过浮点型V=(-1)^S * M * 2^E是把科学计数法的10换成了2。)

回到正题,M就是将浮点数转换成二进制再转换成类似于科学计数法的形式时,5.5的部分,E就是小数点的位移量,

同时,计算机又根据32为浮点型(float)与64位浮点型(double)将存储方式分类两类:

32位浮点型:第一位位符号位(s),往后8 bit表示E,剩下23 Bit表示M

64位浮点型:第一位表示符号位,往后11 bit表示E,剩下52 bit表示M。

关于M:

M,的取值范围:M大于等于1,小于2。

也就是说,M是默认存在一个1的,所以,为了节省一位有效数字,我们在把M存储到内存中时,只会存储小数部分,当要使用该浮点型的数时,提取M,只需要在前面加上一个1就行。假设是一个32位的浮点数,以这样的方式存储,原本只能存放23位有效数字,现在就能存放24位有效数字了(存放到内存中的小数部分(23位)与默认的1,一共24位)

关于E:

上面我们说到,V的公式类似于科学计数法,但是在科学计数法里,E的值可以为负数;但是在V公式中,E是一个无符号整型,它只能为正数,所以,为了防止出现负数的情况,在计算机把E存入到内存中时,会让E加上一个中间值。

32位的浮点型中间值为127

64位的浮点型中间值为1023

比如上面提到的V=5.5=101.1=(-1)^0* M *2^2;

E=2,加上中间值=129,再转换成二进制形式:1000 0001。

如果要取出来,也很简单,让E减去中间值就行了。

但是在提取E时,会出现三种情况:

  • E全为0的情况
  • E全为1的情况
  • E不是全为1也不是全为0的情况(正常情况)

E全为0的情况:(假设是32位浮点数)

1
s: 0  E: 00000000  M: 00000000000000000000000

此时E是存储在内存中的,E=0,我们知道E存储到内存中时要加上中间值,前面已经知道了32位浮点数的中间值是127,当E为多少时,加上中间值会等于0?答案是:-127

所以 : v=(-1)^S* M*2^E,此时不管S与M等于多少,最终V都是一个接近于0的数。

E全为1的情况

1
s: 0  E: 11111111  M: 00000000000000000000000

此时E是存储在内存中的,E转换成十进制是255,我们知道E存储到内存中时要加上中间值,前面已经知道了32位浮点数的中间值是127,当E为多少时,加上中间值会等于255?,答案是:128。

所以 : v=(-1)^S* M*2^E,此时不管S与M等于多少,最终V都是一个非常大的数。

E不是全为1也不是全为0的情况(正常情况)

正常情况就不赘述了。当你看完了前面的内容,这种情况也就能自己去推算了。

现在我们在回到前面的问题:

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
int n=9;
float *pFloat =(float*)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);

*pFloat=9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}思考下这段代码输出的值是什么?

当面把&强转成浮点型时,9=(-1)^S* M *2^E;

S=0、E=00000000、M=00000000000000000001001。

所以最后输出:

1
printf("*pFloat的值为:%f\n",*pFloat);

值为:0.000000

在当我们给*pFlota赋值9.0时;

9.0=(-1)^S* M* 2^E;

S=0、M=001、E=3,

转换成二进制:0 10000010 00100000000000000000000,再转换成十进制就等于:1091567616