浮点数在内存中的存储详解(超详细)

211da4f687d9491f828be3be8f1d8e6a.png

目录

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。

2531fdd30fda42e8af2213844fd0dfb5.jpeg
对于64位双精度数,最高一位为S,接着的11位是指数E,剩下的52位为有效数字M。

3df9bb3d622e4c7aa8cc8fef7db3f745.jpeg


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 = 0
	E需要在真实值上+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 得到真实值 -126
	M不再加1,而是还原成0.xxxx,所以M此时为0.001
	相当于原码为 0.001的小数点向右移动-126位
	是一个极其接近0的小数
	*/
}	

 情况3:E全为1

当E全为1时,表示特殊值。如果M全为0,表示±无穷大(正负取决于符号s),如果M不全为0,表示这不是一个数(NaN)。

8fdd48caf53e46a897fd8a5554dbb454.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值