float和double类型在计算机中是怎么储存的

时间:2023-02-21 22:01:55

我们先看一个代码。

#include<stdio.h>
int main()
{
int n = 9;
float* pfloat=(float*)&n;
printf("n的值为:%d",n);
printf("*pfloat的值为%f\n",*pfloat);
*pfloat = 9.0;
printf("num的值为%d\n",n);
printf("*pfloat的值为%f\n",*pfloat);
return 0;
}

在我刚开始遇到这个代码的时候我认为的输出值应该全为9但是运行结果却是

如图所示

float和double类型在计算机中是怎么储存的

那为什么会出现这种情况呢?我们一个一个的分析。

#include<stdio.h>
int main()
{
int n = 9;//在这里我们定义了一个整型的变量并初始化为9
float* pfloat=(float*)&n;//在这里我们将n的地址强制转换为float类型并且存入到这个字符指针类型中
printf("n的值为:%d",n);//在这里是%d即我们以整型的规律打印n而n确实就是整型所以这里我们打印9是毫无问题的
printf("*pfloat的值为%f\n",*pfloat);//但这里就出现了问题为什么呢?
*pfloat = 9.0;
printf("num的值为%d\n",n);
printf("*pfloat的值为%f\n",*pfloat);
return 0;
}

我们就要去了解一下关于计算机是怎么储存float和double类型的数据了。根据国际标准IEEE754任意一个二进制浮点数v都可以表示成下面的形式

(-1)^S*M*2^E

 (-1)^S表示符号位,当S=0,v为正数;当S=1,V为负数。

 M表示有效数字,大于等于1小于2.、

2^E表示指数位。

以1.5为例我们将其转化为2进制,在.以前就是1在小数点以后也是1因为小数点以后每一位的权重从开始到最后分别是2^-1 2^-2由此类推.所以1.5二进制就是1.1。可以写成(-1)^0 * 1.1 *2^0.

但是这种储存方式就会导致一些浮点数例如5.3这个数就绝对不可能准确的被保存于计算机中。我们将5.3转为2进制小数点之前就是101就把5表示出来了但是0.3却难以表示出来,首先小数点后一位我们只能填0因为第一位权重为0.5大于0.3,那就第二位为1为0.25那对于0.3我们就还差0.05但第三位又不是0.05只能继续往下。所以部分浮点数在计算机内部是难以和原值保持一致的。

所以计算机对于一个浮点数来说储存浮点数的时候计算机储存的就是S和M和E。

然后对于计算机来说float类型的数据占四个字节。32个比特位。

计算机就把首位用来储存S之后的八个比特位用来储存E接下来的23个比特位用来储存M。

对于double类型的数据首位依旧用来储存S之后的11个比特位储存E最后的52个比特位储存M。

下面就是单精度浮点数的内存储存模型

float和double类型在计算机中是怎么储存的

然后对于M和E他们储存的方式也不同。首先对于电脑来说E为一个无符号数它的取值范围为0到255

但是在实际情况中有时候确实会存在E是负数的情况;例如0.5转为2进制为0.1可以写成1*2^-1此时E就为-1.所以按照规定float类型E我们需要加上中间数127而double类型我们需要加上1023然后再将这个数改为2进制存入到那八个比特位中。

对于M根据IEEE754的规定在计算机内部保存M的时候默认这个数的第一位总是1因此可以被舍去只保存后面的部分例如1.01计算机就只会保存01即小数点后面的位数我们以5.5f作为例子首先改写成2进制就是101.1改写成IEEE754规定的就是(-1)^0 * 1.011 *2^2

S为0 E为2但是要加上中间值那么E就是129最后M就是01

在计算机内存中就是0//这是S 10000001//这是E,最后的23位前两位就保存01然后再以0补缺失的位

所以M就是01000000000000000000000将这三个保存起来就是5.5f在电脑中的储存形式

01000000101100000000000000000000

转成16进制40b00000

然后我们用代码来验证

#include<stdio.h>
int main()
{
float a=5.5f;
return 0;
}

我们通过调试的内存看a图下

float和double类型在计算机中是怎么储存的

现在我们就可以去解释上面的那个代码了。

取出这个值的时候由于我们遇到的E情况不同取出的规则也是不同的首先如果E为全零此时浮点数的指数就是-127那么取出时

有效数字M不再加第一位的1而是还原成0.xxxxxx的小数这样是为了表示+-0,以及接近于0的很小的数字。

E为全1时有效数字M全为0表示无穷大

以上两种都是特殊情况一般E既不是全0也不是全1那取出规则就是E的计算值减去127(或是1023)

得到真实值。最后有效数字M前加上第一位的1.

#include<stdio.h>
int main()
{
int n = 9;//我们先来写出9的补码(因为9是正数,原码,补码,反码都是一样的)00000000000000000000000000001001
float* pfloat=(float*)&n;
printf("n的值为:%d\n",n);
printf("*pfloat的值为%f\n",*pfloat);//在这里由于我们是用*pfloat打印的数所以计算机就会认为这一串补码是按照浮点数的规则那么首位就是0后八位为0减去127即为真实值从这我们就可以知道为何打印出的是0了-127即1/2^127更何况还要再乘上后面的那个极小的数所以这里打印的是0
*pfloat = 9.0;//而这里对于计算机而言保存这个9.0就是以浮点数的形式保存的正数所以S为0 然后9.0写成2进制就是1001写成1.001*2^3所以E就是130(加了中间值)最后去除首位的1后001放进M中
所以这个9.0在计算机内部储存的格式就是01000001000100000000000000000000
printf("num的值为%d\n",n);//这里我们是以整形的方式打印n的而通过指针n内的数据已经变成了上面的这个二进制代码这个转化成二进制就是1091567616而运行出的答案正好也是这一个
printf("*pfloat的值为%f\n",*pfloat);
return 0;
}

下面就是这个代码的运行打印情况

float和double类型在计算机中是怎么储存的

如果有错误欢迎各位指出。