首先需要知道:二进制补码表示的正数实际上左侧有无限多个0,而负数有无限多个1.只是为了适应硬件的宽度,二进制表示的数的前导位被隐藏了。
下面看一下64位的 − 4 t e n -4_{ten} −4ten的补码(以64位机器(机器里面存的是补码)为例):
00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 0000010 0 t w o = [ 4 ] 补 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000100_{two}=[4]_{补} 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000100two=[4]补
11111111 , 11111111 , 11111111 , 11111111 , 11111111 , 11111111 , 11111111 , 1111110 0 t w o = [ − 4 ] 补 11111111,11111111,11111111,11111111,11111111,11111111,11111111,11111100_{two}=[-4]_{补} 11111111,11111111,11111111,11111111,11111111,11111111,11111111,11111100two=[−4]补
下标two表示二进制,下标ten表示十进制。
我们把 [ − 4 ] 补 [-4]_{补} [−4]补和 [ 4 ] 补 [4]_{补} [4]补相加会得到什么呢?
[ − 4 ] 补 + [ 4 ] 补 = 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 0000010 0 t w o + 11111111 , 11111111 , 11111111 , 11111111 , 11111111 , 11111111 , 11111111 , 1111110 0 t w o = 1 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 00000000 , 0000010 0 t w o [-4]_{补}+[4]_{补}=00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000100_{two}\\+11111111,11111111,11111111,11111111,11111111,11111111,11111111,11111100_{two}\\=1,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000100_{two} [−4]补+[4]补=00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000100two+11111111,11111111,11111111,11111111,11111111,11111111,11111111,11111100two=1,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000100two
我们发现 64位补码相加得到 2 64 2^{64} 264!
下面求8位的 − 4 t e n -4_{ten} −4ten的补码
已知 有符号数 − 4 t e n = 1000010 0 t w o -4_{ten}=10000100_{two} −4ten=10000100two,
根据口诀:负数的补码=负数的原码取反(符号位不变,数据位取反)+1
可以得到: [ − 4 ] 补 = 1111101 1 t w o + 0000000 1 t w o = 1111110 0 t w o [-4]_{补}=11111011_{two}+00000001_{two}=11111100_{two} [−4]补=11111011two+00000001two=11111100two,
而 4 t e n 4_{ten} 4ten的补码仍然是原码,即 [ 4 ] 补 = 0000010 0 t w o [4]_{补}=00000100_{two} [4]补=00000100two,
我们把 [ − 4 ] 补 [-4]_{补} [−4]补和 [ 4 ] 补 [4]_{补} [4]补相加会得到什么呢?
[ − 4 ] 补 + [ 4 ] 补 = 1111110 0 t w o + 0000010 0 t w o = 1 , 00000000 [-4]_{补}+[4]_{补}=11111100_{two}+00000100_{two}=1,00000000 [−4]补+[4]补=11111100two+00000100two=1,00000000
我们发现 8位补码相加得到 2 8 2^{8} 28!
二进制补码得名于下述规则:一个n位的数与其n位的相反数做无符号加法,结果为 2 n 2^n 2n,因此,x的相反数(或相补数)-x 等于 2 n − x 2^n-x 2n−x,或叫“二进制补码”。
以n=8位为例,1的二进制表示为00000001,则 -1的二进制补码可以由上面的公式写出来
[ − 1 ] 补 = 2 n − 1 = 2 8 − 1 = 1 , 00000000 − 00000001 = 11111112 − 00000001 = 11111111 [-1]_补=2^n-1=2^8-1=1,00000000-00000001=11111112-00000001=11111111 [−1]补=2n−1=28−1=1,00000000−00000001=11111112−00000001=11111111
注:这里11111112仅仅是方便计算,实际二进制中逢二进一,还是100000000.
同样的,我们来看-128的情况。
对于 12 8 t e n = 2 7 = 10000000 128_{ten}=2^7=10000000 128ten=27=10000000 ,这里的n=8,根据补码的定义:
[ − 128 ] 补 = 2 8 − 128 = 1 , 00000000 − 10000000 = 11111112 − 10000000 = 10000000 [-128]_{补}=2^8-128=1,00000000-10000000=11111112-10000000=10000000 [−128]补=28−128=1,00000000−10000000=11111112−10000000=10000000
综上,求1个负数(-x)的补码,只需要用 2 n − x 2^n-x 2n−x即可,这里的n表示这个负数的二进制表示位数。
后记:这个问题在《计算机组成与设计-硬件/软件接口》一书中有详细的解释。