目录
小心!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 = 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)。