目录
一.前言
二.问题的引出
三.两类浮点型数据(float、double)在内存中的存储方式
3.1两类浮点型数据的存储模型
3.1.1浮点型数据数值读取的通用模型
3.1.2float类型数据和double类型数据在内存中的存储模型
3.2对浮点型数据存储模型的详细解读
3.2.1对于符号位S的解读
3.2.2对指数位E的解读
3.3.3对M位的解读
3.4浮点型数据存储案例
3.5 E在内存中取出的三种不同情况
3.5.1 E的二进制位不全为 0或 1
3.5.2 E的二进制位全为 0
3.5.3 E的二进制位全为 1
四.引出问题的解答
五.总结
一.前言
在我之前的博客【C语言】整型在内存中的存储方法_昊子的博客-CSDN博客中提到数据类型分为了四类:整型、浮点型、指针类型和空类型,并且讲到了整型在内存中的存储方式。相对于整型数据,浮点型数据在内存中得存储方式则更加复杂。本文共五章内容,在第二章给出了一段代码,引出浮点型数据的存储问题,第三章详细介绍了两类浮点型数据(float类型和double类型)在内存中的存储模型,第四章对第二章提出的问题进行解答,最后一章对全文进行了总结。
二.问题的引出
下段代码:若按照类型转换进行理解,三个printf应该分别输出9、9.000000、9,但运行结果现然与预期不符,为什么?
这里,就设计到了浮点型数据和整型数据之间存储方式的差别。对于第一个printf,直接打印整型的n,显然没有问题。但对于第二个printf,解应用指向n的指针pn,pn是浮点型指针,对其解应用并打印,是以浮点型数据存储方式的视角去读取数据。而对于第三个printf,n1以浮点型数据的存储方式存入内存,以整型数据的视角解应用指向n1的指针pn1。这就解释了为什么实际结果与预期结果有很大的差异。
第三章中详细解释了浮点型数据的存储方式,可用于解释当前的问题。
int main(){int n = 9;float* pn = (float*) & n;printf("%d\n", n); //输出9printf("%f\n", *pn); //输出0.000000float n1 = 9.0;int* pn1 = (int*)&n1;printf("%d\n", *pn1);//输出1091567616return 0;}
三.两类浮点型数据(float、double)在内存中的存储方式
3.1两类浮点型数据的存储模型
3.1.1浮点型数据数值读取的通用模型
根据IEEE754标准规定,浮点型数据的存储和读取按照公式:
Value为浮点型数据的二进制值S表示浮点型数据的正负,0表示正,1表示负M为位于位于区间的小数E表示为指数部分
举例说明具体应用方式:对于float n = 10.5,其二进制表示为(00....00001010.1),可理解为,其中S=0,M=1.0101,E=3。
3.1.2float类型数据和double类型数据在内存中的存储模型
图3.1为单精度浮点型数据float的存储模型,符号位S存在最高位,占用1bit内存,指数位E占用8bit内存,M占用23bit内存。
图3.2为双精度浮点型数据double在内存中的存储模型,符号位S占用1bit内存,指数为E占用11bit的内存,M占用52bit的内存。
3.2对浮点型数据存储模型的详细解读
3.2.1对于符号位S的解读
符号位表示浮点型数据的正负,0表示正数,1表示负数,如10.5的符号位S为0,-10.5的符号位S为1。
3.2.2对指数位E的解读
指数位E即2的次方数,如 float n = 10.5,可以表示为。这里,E = 3。但是,IEEE754标准中规定E为一个无符号整型,而现实中科学计数法指数位E可能为负数,因此,这里引入了中间值的概念。对于单精度浮点型数据float,E的中间值为127,对于双精度浮点型数据double,E的中间值为1023。在对E进行存储时,需要将其真实值加上中间值后再存入内存。如,10.5的指数位真实值E = 3,则存入内存内存应为E = 3 + 127(中间值) = 130。再比如,真实指数位E = -2时,存入内存应为 E = -2 + 127 =125。
3.3.3对M位的解读
M位可理解为有效数据,其取值范围是,因为M小数点前面的数永远为1,为了增加浮点型数据的精度及可表示的数据的范围,IEEE754标准规定M在内存中只存储小数点后边的位,如float n =10.5表示为,M = 1.0101,存入内存时舍去小数点前面的1,仅存入0101。
3.4浮点型数据存储案例
这里以前文多次提到的单精度浮点型数据10.5在内存中的存储为例,展示其在内存中的具体存储细节,便于理解存储模型。
符号位有效数据位真实指数位,存入内存中的指数位,二进制表示为1000 0010
3.5 E在内存中取出的三种不同情况
3.5.1 E的二进制位不全为 0或 1
将二进制表示的E转化为其对应的十进制,再减去中间值从而得到真实的E。将M的有效位补1。
3.5.2 E的二进制位全为 0
这是指数E的真实值为(0 - 127)或(0 - 1023),有效数M的前面不再补0,而是表示为0.xxxx,此时的浮点数表示为,即无穷小的数。
3.5.3 E的二进制位全为 1
表示为无穷大的数(与M的情况无关)。
四.引出问题的解答
第二章展示的代码的第二个printf打印结果为0.000000,是因为其以浮点数的视角去读取内存中的数据。图4.1展示了整型数据 int n = 9在内存中的存储形式,若以浮点型数据的视角去读数,则,,E的二进制位全部为0,由3.5.2中提到的知识,E的二进制位全为0时表示为一个无穷小的数据,且以%f 的形式打印数据精确度为小数点后6为,故打印0.000000。
第三个printf打印的数据为1091567616,一个非常大的数据。这是因为 n1 = 9.0 以浮点型数据的存储方式存入到了内存中(存储方式如图4.2所示),但在读取并打印这个数据的时候,却是对一个整型指针进行解应用,将9.0以整型数据的视角进行读取和打印。
故打印结果为:。
五.总结
本文详细介绍了单精度浮点型数据和双精度浮点型数据在内存中存储的方法,给出了浮点型数据的存储模型和读取模型,并以案例的形式进行了介绍。浮点型数据可表示为:。其中为符号位,在内存中占用8bit的空间;为指数位,在内存中占8bit(float类型数据)或16bit(double类型数据)的内存空间;表示有效数组在内存中占23bit(float类型数据)或52bit(double类型数据)的内存空间。