这是一篇重点介绍浮点型数据在内存中如何存储的博客,对同一种数据类型以不同方式取出导致的不同结果,进行了仔细解释。
一、介绍背景
简单举例:
#include<stdio.h>
int main()
{
int a = 7;
float* b = (float*)&a;
printf("a = %d\n", a);
printf("b = %f\n", *b);
float c = 7;
int* d = (int*)&c;
printf("c = %f\n", c);
printf("d = %d\n", *d);
}
运行结果如下: 由此可知:二进制中,浮点型数据和整型数据存储的方式是不同的,因为若是相同,则输出数据应都为7,而结果显然不是。
二、原理介绍
1、整型
整型数据在内存中以补码存储,在这篇博客中,有我的仔细介绍以及二进制转换的运算方式——>原反补详解。
2、浮点型
浮点型数据,也就是带小数点的数据,对应的数据类型有float、double、long double等; 为了便于存储,浮点数在内存中的存储与整数并不相同,而是采用二进制的科学计数法,以符号位S、指数位E、数据位M三块,在内存中划分三块区域分别存储;由此,我们需要先介绍——>
a、二进制中的科学计数法
以十进制的科学计数法类比二进制——> 举个例子: 在本例中,符号位S即为0,数据位M为1.27,指数位E则为2。
- 符号位S 0表示正,1表示负;
- 有效数字M 十进制中,有效数字M小数点前的数必须满足大于等于1,小于10,也就是说: 1≤ M < 10; 同理, 二进制中的有效数字M中,小数点前的数必须大于等于1,小于2,所以M恒为 1.xxxxxx;故存储时,可将小数点前的1省去,只存储小数点后的数据;
- 指数位E 指数位在存储时为——无符号整型,但指数本身又有正负之分,因此取中间数作为0值,计算时,需减去中间数;
b、浮点数在计算机中的存储
- 对于32位的浮点数,最高位为符号位S,紧接着的8位是指数位E,剩下的23位为数据位; 如下图:
- 对于64位的浮点数,最高位为符号位S,紧接着的11位是指数位E,剩下的52位为数据位。 如下图:
c、二进制中须注意部分
- 指数位E的中间数 根据国际标准IEEE(电气和电子工程协会)754规定: 指数位E作为无符号整型,但由于E有正负,所以在E为8位时,中间数是127,11位时中间数为1023。
- 数据取出的三种情况:
-
- 指数位E全为0时: 由于此时值已经是接近于0的数字,因此规定这时,浮点数的指数E等于1-127(或者1-1023)即为真实值, 有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字;也就是指数+1,有效数字M前的1舍去,以接近0;
-
- 指数位E全为1时: 这时,如果有效数字M全为0,则表示 ±无穷大(符号取决于符号位S);
-
- 指数位E不全为0(也不全为1)时: 指数E减去127(或1023)则为真实值,再将有效数字M前加上1,进行科学计数法的运算即可。
三、十进制与二进制的转换
1、精度损失
二进制中的0.1 = 十进制中的0.5,这是因为 $$2^{-1} = 0.5$$ 同理: $$2^{-2} = 0.25$$ $$2^{-3} = 0.125$$ $$2^{-4} = 0.0625$$ ::: hljs-center
······
::: 在转换时,可以将十进制中的小数部分乘以2作为二进制的一位,然后继续取小数部分乘以2作为下一位,直到不存在小数为止。 以0.3为例: $$0.3×2 = 0.6 ·····0$$ $$0.6×2 = 1.2 ·····1$$ $$0.2×2 = 0.8 ·····0$$ $$0.8×2 = 1.6 ·····1$$ $$0.6×2 = 1.2 ·····1$$ $$0.2×2 = 0.8 ·····0$$ ::: hljs-center
······
::: 因此, 十进制下的0.3无法被精确表示成二进制小数,这也是为什么十进制小数转换成二进制小数时会出现精度损失的情况。
2、回到博客背景
-
整形中,十进制的7存储为补码,原反补相同,因此为: 00000000 00000000 00000000 00000111 而此时,若将此数据强制转换为浮点型float,且以此输出,则: 符号位S为 0 指数位E为 0000000 0 有效数字M为 0000000 00000000 00000111 转换为十进制则为 0.00000000000000000000111 如图: 由于printf以%f打印时默认打印前6位,因此打印出数据位0.000000;
-
而在浮点型中,十进制的7.0为二进制中的111,即$(-1)^0 × 1.11 × 2 ^ 2$ 因此存储为 符号位S为 0 指数位E为 2+127=129 即 10000000 1 有效数字M为 1100000 00000000 00000000 如图: 此时若以访问整型的方法访问此变量,且以整型输出 则此值为: 01000000 11100000 00000000 00000000 即十进制中的108842188。