为什么整数数据在内存中存的是补码?
为了简化计算机基本运算电路,使加减法都只需要通过加法电路实现,也就是让减去一个正数或加上一个负数这样的运算可以用加上一个正数来代替。
char在内存中存的是整形
signed char 的范围是 -128~127
00000000
00000001
…………
01111111 //0~127
10000000
10000001
…………
11111111
10000000//-1~-128
补码
其实就相当于:
超出范围时,对最大值取余比如说无符号char 存了个256 实际上对应的就是256%255=1,余了个1相当于范围的第一个,根据起始的范围不同而不同。0-255 所以余1就是对应0,256在unsigned char 中存的是0。
unsigned char 的范围是 0~255
数据范围是多少就看bit位数有多少。
大小端字节存储:
大端字节存储:低位字节处数据存放在高地址处,高位字节处数据存放在低地址处。
小端字节存储:低位字节处数据存放在低地址处,高位字节处数据存放在高地址处。
练习
下面程序输出什么?
1.
//输出什么?
#include <stdio.h>
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d,b=%d,c=%d",a,b,c);
return 0;
}
char 跟signed char unsigned char 实际上在内存存的补码都是一样的,区别在于:
整形提升时,无符号char会按0补位;有符号char按符号位补位
打印时,按u打印,按c打印,即不需要补位时:
哪怕是无符号char也可以打印出有符号,有符号char也可以打印无符号
2.
#include <stdio.h>
int main()
{
char a = -128;
printf("%u\n",a);
return 0;
}
思路
char a = -128
-128原码:10000000 0000000000 0000000000 10000000
反码:11111111 11111111 11111111 0111111
补码:11111111 11111111 11111111 1000000
存到char类型的a中需要截断
即a中的补码为:100000000
题目中%u为整数格式,因此打印的时候需要整数提升
而a又是有符号char,因此前面补符号位
a提升后的
补码:11111111 11111111 11111111 10000000
%u为无符号,因此补码即原码,直接打印
转为10进制就是4294967168
结果应为4294967168
浮点数在内存中的存储
接下来根据一串代码来理解浮点数在内存中的存储。
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
分析前两句,就是如图。将n的地址转成float* 存放在pFloat里
float指针解引用时,会认为内存放的是float型数据,打印时结果是0.000说明浮点型存储方式跟整形不同。
可以这样理解
第二、四:以浮点型方式从内存拿出来。
第一、三:以整形方式从内存拿出来。
前两句在内存中是按整形存放,后两句是按浮点型方式存放。
第二句在内存整形数据以浮点型方式取出,输出的数据不是9.0;
第三句在内存浮点数据以整型方式取出,输出的数据不是9。
综上说明浮点型数据跟整形数据的存储方式不同。
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位
如 5.5,表示为(-1)^0*101*2^2
小数位的二进制如下:
小数的二进制
0.1------->2^(-1) = 1/2^1 (0.5)
0.01------>2^(-2) = 1/2^2 (0.25)
0.001----->2^(-3) = 1/2^3 (0.125)
0.0001---->2^(-4) = 1/2^4 (0.0625)
因此小数位一个1,就表示1/2=0.5
转为科学计数法,M*2^E,小数点向左移动几位E就等于几,相当于20变成2*10^1
浮点数在内存中可能没法精确保存,比如存3.14,小数位的二进制可能无限逼近0.14而又没法准确的存入。
IEEE 754规定:
对于32位的浮点数,最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
对于E,存放的数据是无符号数据,但E是有可能为负数的,因此规定8位的+127再放进去 11位的+1023再放进去
比如0.5转成二进制:0.1,再按科学技术表示 →(-1)^0 * 1.0 * 2^(-1)
S=0
E=-1,加上127=126,再存进去(二进制为 01111110)
M=1.0 (23位)
存放:S:0 E:01111110 M00000000000000000000000
0.5在内存存储的二进制:00111111000000000000000000000000
E全0表示无穷小:E=1-127=-126,就是2^(-126),是非常小的数(-127相当于还原)
E全1表示无穷大:E=255-127=128,就是2^(128),是非常大的数
回到之前的练习,int 9在内存中存的是000000000 00000000000000000001001
第二条用float方式取出,那么上面的补码被解读成
S=0,E=00000000(8bit),M=0.00000000000000000001001
即:(-1)^0* 0.00000000000000000001001 *2^(-126)
而float打印时打印到第6位,而此时小数已是无限接近0的数字,根本找不到后面的有效数字,故最后打印的是0.000000
再看float 9.0,在内存中存的是 1001.0→ 1.001*2^3
S=0,E=3,M=001(舍去小数点前的1)
按规定存进内存:E+127=130,M后面补20个0 (23bit)
0 10000010 00100000000000000000000
浮点数的存储以浮点型方式拿出,是没有问题的。第三条以int型拿出,因为是正数,原反补码相同,通过计算器换算得到结果1091567616,跟运行结果一样。
浮点数的比较
浮点数因为精度问题,是没法直接和某些数比较的。
float f=0.12;
if(f==0.0)