补码
以8位数据为例, 对应的所有二进制可能是00000000b~11111111b。
对于无符号整数而言:上面的二制数是与0~255一一对应的。
00000000b: 0
00000001b: 1
00000010b: 2
……
11111111b:255
如果想表示负数(有符号数),怎么办?
原码,反码,补码,都是表示方法。后两者由前者发展而来。
原码表示法:最高位符号位(正为0,负为1),其余用作数值
反码表示法:正数约定与原码表示法相同表示,对应负数则将每位取反
补码表示法:正数约定与原码表示法相同表示,对应负数则为负数反码表示方式+1
一
人们首先想出了一种办法:用最高位来表示正负,其它位来表示数值。
这个就是原码的表示:
00000000b:0
00000001b:1
……
01111111b:127
10000000b:0
10000001b:-1
……
11111111b:-127
可以看出有两个表示0的二进制。
二
于是有了第二种想法:
用00000000b~01111111b表示0~127,然后再用它们二进制码全部取反来得到其对应的负数。
这个就是反码的表示:
00000000b:0 11111111b:-0
00000001b:1 11111110b:-1
…… ……
01111111b:127 10000000b:-127
可是同样的问题还是再次出现了。0又出现了两次。
三
让0只有00000000b一种二进制码,发现可以将原来的反码加一。因为8位寄存器最多只留8位。所以加1后就正好是00000000b
00000000b:0 11111111b + 1 = 00000000b:-0
00000001b:1 11111110b + 1 = 11111111b:-1
…… ……
01111111b:127 10000000b + 1 = 10000001b:-127
010000000b:128 01111111b + 1 = 10000000b:-128
这样就正好形成一个闭环(用了四位二进制来表示)。而且最最最重要的是,可以实现符号相反的数相加的和的补码为0(除去溢出的那位)。
1+(-1) = 1 0000 因为是四位,所以这个最高位的1不算在内。正好是0的补码!
也可以说明,补码的运算与字长非常相关,这里字长为4位。
如果字长为8位:
1 + (-1) = 00000001 + 11111111 = 1 00000000
对于此圆,顺时针表示加1,逆时针表示减1,在圆内异号的数运算结果不会出此圆。
有了这个结论,以后的减法就可以用加法来实现了。
比如:
3 - 2 = 0011 - 0010 CPU才不会这么算,他只会加法,3逆时针移动2位/-2顺时针移动3位。
3 + (-2) = 0011 + 1110 = 1 0001 最高位也不要了,正好是1
一个正数最高位为0时,取其反码+1, 为其负数,所以:
就一个补码而言,最高位为1的就是负数,为0的就是正数,0的补码就全是0。
正数的补码就是本身的原码
负数的补码就是其对应正数原码(也是正数补码)的反码加1
例如:
20的补码:00010100b
20的反码:11101011b
-20的补码:11101100b
可以不通过正数的反码来求其负数的补码:
将正数补码从右向左复制,当复制完一个1后,后面的全部取反。
例如:
5的补码:0101
-5的补码:1011
因为一个正数,必有一位会是1,此位取反后会是0,此位加1还是1,不会进1给高位了,高位就一直是它的反码了。反之,如果一位是0,那取反后是1,加1会向高位进1,高位就是反码+1,变成本身了。所以从低位起遇0复制,再复制一个1,后面取反即可。