hello大家好,我是柚子,今天给大家分享的内容是整数、浮点数在内存中的存储,以及牵扯到的存储顺序的问题(大端和小端)。
一、整数在内存中的存储
①整数的二进制表示有三种,即原码、反码和补码。有符号的整数,三种表示方法均由符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,最高的一位是被当做符号位,剩余的都是数值位;
②正整数的原码、反码、补码都相同;
③负整数的三种表示方法各不相同:原码:按照正负数值直接转化为二进制即可得到
反码:原码符号位不变,数值位依次按位取反
补码:反码+1
例如下图:
对于整型来说:数据存放内存中其实就是存放的补码。
为什么?
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码可以将符号位和数值域统一处理,同时加减法也可以统一处理(CUP只有加法器,-1==+(-1),+(-1)这种运算只有补码才能正确计算)此外,补码与原码相互转换(取反+1),其运算过程是相同的,不需要额外的硬件电路。
大端/小端字节序存储
什么是大端、小端?
其实超过一个字节的数据在内存中存储的时候就有存储顺序的问题,按照不同的存储顺序,我们分为大端字节序存储和小端字节序存储:①大端存储模式:数据的低位字节内容保存在内存的高地址处,高位字节内容保存在内存的低地址处;②小端存储模式:数据的低位字节内容保存在内存的低地址处,高位字节内容保存在内存的高地址处。
如何判断?我们来用代码实现一下:
二、浮点数在内存中的存储
我们先来 看一道例题,先别看答案,自己判断一下预期的输出结果;
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
好,我们接下来来看一下实际输出结果:
按照注释,是否能理解了一丢丢,这么看来整数的存储方式和浮点数的存储方式是不一样滴。
具体情况我们一起来看一下!
根据国际标准 IEEE (电气和电子工程协会)754,任意一个二进制浮点数V可以表示成下面的这种形式:
例如:①十进制的5.0,写成二进制是101.0,相当于1*1.01*2^2;
S=0,M=1.01,E=2 。
②十进制的5.0,写成二进制是-101.0,相当于(-1)*1.01*2^2;
S=1,M=1.01,E=2 。
IEEE 745规定:
对于32位浮点数,最高位1位是存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M
对于64位浮点数,最高位1位是存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M
a.浮点数存的过程
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的XXXXXX部分,比如保存1.01的时候,只保存01,等到读取的时候再把第一位加上去。这样做的目的是节省一个有效数位,以32位为例,留给M的只有23位,将第一位的1舍去以后,等于后边可以多保存一位,这样更精确。
但是如上图所展示的黄色部分的E,规定成无符号的整数,但是E它也有可能是负数,(比如0.5,E为-1),那怎么办呢,这下没办法存进去了,出现bug了,不要着急,这时我们可以看看那个IEEE 754是怎么规定的,它说存入内存的E的真实值必须再加上一个中间数,8位的E的取值范围是0~255,11位的E的取值范围是0~2047;所以,对于8位的E,这个中间数是127,对于11位的E,这个中间数是1023。
如果理解起来太抽象的话,我们看例题:
到此我们可以调试来验证一下:
b.浮点数取的过程
上述三种情况的后两种简单了解一下就可以了,重点是第一种常规复原的方法!!
到这儿之后我们再回过头去看这个例题:
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
//9的二进制1001
//补码:000000000000000000001001
//0 00000000 00000000000000000001001
//S=0,E=-126,M=0.0000000000000000001001
printf("*pFloat的值为:%f\n", *pFloat);//0.000000
*pFloat = 9.0;
//9.0 二进制1001.0
//S=0,E=3,M=1.001
//0 10000010(127+E) 00100000000000000000000
//01000001000100000000000000000000 整型去取的时候会认为这32位是补码
printf("n的值为:%d\n", n); //1091567616
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
这么解释是不是清楚了很多,恍然大明白的感觉。
我们来总结一下:①有些浮点数在内存中可能无法精确保存(例如3.14,99.7)
②double类型的精度比float类型更高
③两个浮点数在比较大小的时候,直接使用“==”可能会存在问题!
如果非要比较的话,就添加一个精度,例如:if(abs(f-5.6)<= 0.000001),推荐一本书《C语言深度解剖》,这里边有详细记载如何比较两个浮点数。
ok,今天的分享就到这里,我们下次不见不散,,关注柚子不迷路哦!!