1.原码
原码就是早期用来表示数字的一种方式: 一个正数,转换为二进制位就是这个正数的原码。负数的绝对值转换成二进制位然后在高位补1就是这个负数的原码
举例说明:
int类型的 3 的原码是 11B(B表示二进制位), 在32位机器上占四个字节,那么高位补零就得:
00000000 00000000 00000000 00000011
int类型的 -3 的绝对值的二进制位就是上面的 11B 展开后高位补零就得:
10000000 00000000 00000000 00000011
但是原码有几个缺点,
a.零分两种 +0 和 -0 。
b.在进行不同符号的加法运算或者同符号的减法运算的时候,不能直接判断出结果的正负。需要将两个值的绝对值进行比较,然后进行加减操作,
最后符号位由绝对值大的决定。于是反码就产生了。
2.反码
正数的反码就是原码,负数的反码等于原码除符号位以外所有的位取反
举例说明:
int类型的 3 的反码是
00000000 00000000 00000000 00000011
int类型的 -3 的反码是
11111111 11111111 11111111 11111100
除开符号位 所有位 取反
解决了加减运算的问题,但还是有正负零之分,然后就到补码了
3.补码
正数的补码与原码相同,负数的补码为 其原码除符号位外所有位取反(得到反码了),然后最低位加1.
还是举例说明:
int类型的 3 的补码是:
00000000 00000000 00000000 00000011
int类型的 -3 的补码是
11111111 11111111 1111111 11111101
就是其反码加1
总结:
a.正数的反码和补码都与原码相同。
b.负数的反码为对该数的原码除符号位外各位取反。
c.负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1
d.原码好理解了,但是加减法不够方便,还有两个零(正负0)
e.反码稍微困难一些,解决了加减法的问题,但还是有两个零
f.补码理解困难
4.数在内存中都是以补码形式存在的
对于一个负数-x,它的二进制表示(补码)求法如下:
a.对x的原码进行取反运算
b.将取反运算的结果+1
对于大多数语言来说,char类型的有效范围是-128~127,那么如果我们把128这个超过了char类型表示范围的数赋值给一个char型变量ch,结果会怎样呢?ch的值会变成0,还是其它值?答案是,ch的值会变成-128,因为128超越了最大值127,于是它开始从最小值开始,搜索一个合适的位置。同样的道理,将129赋值给ch,ch的值会变成-127。
c.对绕回和同余的解释
-336的无符号整数是65200呢?书上说是2的补码(书中描述):数字0到32767代表它们本身,而数字32768到65535则代表负数,65535代表-1,65534代表-2,依次类推,因此-336由65536-336,也即65200来表示;
d.同余
很多人并不理解补码。补码就是同余啊。1000000是正128你知道吧,正负128模256是同余的。加减乘可以直接算也是同余的定理决定的,而不是凑出来的巧合,哪可能凑出这种东西?
8位只能表示256个数,0到255,但我还想表示一些负数怎么办呢?就用与该负数同余的正数来表示呗。-1=255,-2=254,等等。
建议脱离算数的思维方式,这其实就是一个环。模任何一个正整数(如256),可以把所有整数分类,比如模256可分256类,0 256 -256...是一类(余0类),1 257 -255...是一类(余1类),等等,这256类可看作环的元素,你看-128和128是同一个类里的(余128类),用一个代表另一个罢了。补码和普通的unsigned integers都是在每类中选一个数,unsigned integers选0到255,补码表示的有符号整数选-128到127,都是一个数恰好对应一个类。
当你明白这一切后,补码就是顺理成章的事。
-1<--->11111111 -2<--->11111110 -126<--->10000010 -127<--->10000001 -128<--->10000000
127<--->01111111 126<--->01111110 125<--->01111101 002<--->00000010 001<--->00000001 000<--->00000000