为什么要用补码?
我们都知道计算机中的负数是用补码来表示的,而负数的补码是原码符号位不变,其他位按位取反再加一。但是为什么必须这样?为什么非要取反再+1?这个定义是怎么来的?
首先我们用我们熟悉的十进制来思考问题,假设现在我们只考虑两位数字的运算,比如56 + (-28) = 56 - 28,这里如果作正常的减法运算,就需要借位,也就是先让"6"减去"8"的时候,发现不够减,所以要向高位“借一位”,所以"5"只能借一位给“6”,然后“5”变成“4”,这样的“借位规则”如果用电路来实现,会很复杂(至于如何用电路实现,感兴趣的同学可以去看《Code》的一到十二章),但幸运的是我们有办法来避免这样的借位:
56 + (-28) = 56 - 28 + 99 - 99 //加99再减99表达式值不变
= 56 - 28 + 99 + 1 - 100 //把-99写成1 - 100
= (99 - 28 + 1) + 56 - 100 //交换一下位置
如上面最后一步所示,如果按照其从左到右的顺序依次运算,是完全不需要借位的,这里的99其实可以看成“两位数字能表示的最大的数值“,如果是三位数字,那就是999。最后在减去100之前,其实已经”溢出“了,这时候只要把最高位舍去就行了。
换到二进制也是一个道理,假设我们只能用八位数字来表示一个数值,那么上面的28写成二进制就是00011100,然后我们要用“八位数字能表示的最大的数值”减去它,就是11111111 - 00011100,这时候你会发现,这样做的结果刚好就是00011100全部都取反一下(0变1,1变0),变成11100011,然后再加一变成11100100,这时候再加上56(也就是二进制的00111000):
11100100
+ 00111000
------------
100011100
然后把结果中多出来的一位最高位舍去,就得到了00011100,也就是十进制的28(56 - 28 = 28)!
写到这里我想大家应该已经明白了,只要把-28(二进制的-00011100)用72(二进制的11100100)来表示,就能直接用加法得到正确结果了。这也是为什么负数的符号位是1的原因,因为,比如用八位二进制来表示一个数值,如果要包含负数的话,那我们只能舍弃一半原来的正数,让这部分正数来表示负数,很自然地我们用“更大的那一半”来表示负数,比如原来可以表示0到256,现在我们就把128到256“分给负数”,剩下的0到127继续表示正数(如果用128到256来表示正数,0到127表示负数,那么0到127和-127到0这部分数值就没人表示了)。所以,“一串01”在计算机中到底表示几,还得看它能不能表示负数。