浮点数在内存中的存储详解(超详细)
目录
1. 浮点数存储规则
2. IEEE754规定:
3. 关于M的说明:
4. 关于E的说明:
5. 关于S的说明:
6.浮点数从内存中取出(三种情况)
情况1:E不全为0或不全为1
情况2:E全为0
情况3:E全为1
小心!VS2022不可直接接触,否则!没这个必要,方源面色淡然一把抓住!顷刻炼化!
1. 浮点数存储规则
大家都知道整型数据是以补码的方式存放在内存中。以下几个概念是需要知道的:
原码,补码,反码都是以二进制形式表示的。
正整数的原码,反码,补码都相同。
负整数的补码=反码+1=原码的符号位不变,数值位按位取反。
但是浮点数不同,浮点数有着一套自己的存储规则。
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
- (-1)^S * M * 2^E
- (-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
- M表示有效数字,大于等于1,小于2。
- 2^E表示指数位。
- E是一个无符号整数。
这里需要记住S,M,E分别代表的是什么
简述:S用于判断正负号,M表示有效数字,E与小数点相关
2. IEEE754规定:
对于32位的浮点数,最高一位为S,接着的8位是指数E,剩下的23位为有效数字M。
对于64位双精度数,最高一位为S,接着的11位是指数E,剩下的52位为有效数字M。
3. 关于M的说明:
无论是单精度float还是双精度double,它们的有效数字M可以写成1.xxxx的形式,但通常1都会省略掉,系统默认为1,这样可以节省一格bit位,比如存放二进制1.01时,M中只会存放01,整数部位1会省略不写
4. 关于E的说明:
E为一个无符号整数(unsigned int),因此如果E为8位bit,它的取值为0-255,如果E为11位bit,它的取值为0-2047
IEEE 754规定,存入内存时,E的真实值必须再加上一个中间数,这个中间数在8位的E中是127,在11位的E中是1023,(其实就是最大取值的一半),比如2的10次方的E,E此时为10,所以保存成32位(float)浮点数时,必须保存成10+127 =137的二进制,即10001001
5. 关于S的说明:
S可以决定小数是正数还是负数,如果存储的是正数,S为0,如果存储的是负数,S为1
我们举出两个例子,来讲解浮点数在内存中的存储:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{float a = 5.5f;/*S = 0 5.5 的二进制为 101.1 ,整数部位1省略不写M = 011因为101.1 = 1.011 * 10的2次方,所以E = 2因为是float型存储,所以E要在真实值上加127,即129,129的无符号二进制为1000 0001,所以E的存储方式为1000 0001[0] [1][0][0][0][0][0][0][1] [0][1][1][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]S E M */float b = 0.5f;/*S = 0;0.5 的二进制为 0.1因为二进制形式规定第一位必须为1,所以可以写成1.0 * 10的-1次方,因此 E = -1,M = 0E需要在真实值上+127,即126,,126的无符号二进制为0111 1110,所以E的存储方式为 0111 1110低地址[0] [0][1][1][1][1][1][1][0] [0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]高地址*/
}
6.浮点数从内存中取出(三种情况)
我们学习了浮点数的存储,那浮点数从内存中取出打印出来又是如何操作的呢?
就像我们知道,整数存储的是补码,使用printf打印出来的是原码
情况1:E不全为0或不全为1
当E不全为0或不全为1时,浮点数在从内存中取出时会将E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1
详细讲解如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{float a = 5.5f;/*低地址[0] [1][0][0][0][0][0][0][1] [0][1][1][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]高地址S E M 取出方式:E此时为小端存储,无符号整数E表示129,取出时用129-127 得到E的真实值 2 ,再将有效数字M前加上第一位的1过程 :1.011 向右移动 E 个小数点,得到最后结果101.1相当于二进制原码为101.1*/float b = 0.5f;/*低地址[0] [0][1][1][1][1][1][1][0] [0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]高地址取出方式:E此时无符号整数表示为126,需要减去127,得到E的真实值-1,再将有效数字M前加上第一位的1,过程: 1.0 向右移动 E 个小数点,表示0.1相当于二进制原码为0.1*/
}
情况2:E全为0
如果E全为0,E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第一位的1,而是还原成0.xxxxxxx的小数
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{/*低地址[0] [0][0][0][0][0][0][0][0] [0][0][1][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]高地址S E M 如果浮点数此时这样存储,想要推出它原本的二进制数E = 1-127 得到真实值 -126M不再加1,而是还原成0.xxxx,所以M此时为0.001相当于原码为 0.001的小数点向右移动-126位是一个极其接近0的小数*/
}
情况3:E全为1
当E全为1时,表示特殊值。如果M全为0,表示±无穷大(正负取决于符号s),如果M不全为0,表示这不是一个数(NaN)。