本文转载自微信公众号《Linux开发那些事》,作者LinuxThings。转载本文请联系Linux开发那些事儿公众号。在计算机中,所有的数都是用二进制表示的,也就是由0和1组成的各种编码。数的表示可以分为原码,原码和补码怎么表示,补码如果没有特殊指令,以下介绍以4位二进制原码为例。为了区分正数和负数,计算机中将二进制的最高位(bit)指定为符号位。等于0时表示正数,等于1时表示负数,剩下的所有低位(bit)都用来表示数值。下图从左到右分别表示+5和-5的反码。正数的反码与其原码相同。负数的反码以原码为基础,符号位不变,值位取反。下图从左到右分别表示+5和-5的补码。补码是在反码的基础上加1,下图从左到右分别是+5和-5的补码。奇怪了,为什么要用补码这种麻烦的方式来表示数字(尤其是负数),是不是很直观?为了理解这个问题,我们以原码、反码、补码的形式模拟二进制加减法,以4位二进制为例说明原码下图列出了所有正、反码的二进制表示4位二进制的负数。黄色二进制0和1的位置用原码模拟进行3+2、6+(-2)、(-1)+(-3)运算。具体运算过程如下:上图中,圆圈内的二进制位在做加法运算时,将前位的结果结转。由于有效二进制数位为4位,因此圈内的二进制数位会因溢出而被自动舍弃(计算过程中仍会使用溢出的二进制数位,结果溢出的二进制数位将被舍弃。)即可从计算过程看,3+2=5是正确的,但是6+(-2)=0和(-1)+(-3)=4的结果都是错误的。因此,虽然原代码直观易懂,转换起来也容易,但是在运算上,正数之间的加法是没有问题的,而负数之间、正数与负数之间的加法都有问题,所以计算机不能用原码来表示数字的反码。下面列出了4个数字所有正数和负数的二进制表示,请看下图。正数的反码就是它自己,负数的反码就是符号位不变,其他位取反。图中黄色二进制位表示符号位是反码模拟出来的。3+2、6+(-2)、(-1)+(-3)运算,具体运算过程如下:同原代码,上图圆圈内的二进制位由于自动丢失溢出放弃,在补码运算中,只有3+2=5正确,其他结果都不正确注:(-1)+(-3)的结果为1010,符号位为1,说明result是一个负数,根据上面的负小数对应的二进制补码表,可以看出它对应的小数是-5。从求补运算的结果来看,正数相加的结果是正确的,负数和负数、正负数相加的结果是错误的。是的,因此,计算机不能用补码来表示数字的补码。说完原码和补码,再来看补码。下图显示了正负补码的二进制表示。用补码模拟3+2,6+(-2),(-1)+(-3)运算,具体运算过程如下:从上面的计算过程可以看出,去除溢出后二进制位,3+2,6+(-2),(-1)+(-3)使用原码和反码计算结果都是正确的,不能解加法运算,但是使用补码来表示,无论是正数之间,负数之间,还是正数与负数之间的加法都可以解决。因此,计算机选择用补码来表示数字,用补码来进行运算。补码的好处是简化了减法计算。补码在加减法处理中不需要因为数的正负而采用不同的计算方法。只要只有一个加法电路,它就可以处理各种有符号和无符号的加法,而减法可以用一个数加上另一个数的补码来表示,所以只要有加法电路和补码电路,可以完成各种有符号的数字。有符号数和无符号数的加减法,在电路设计中,统一表示数字0是相当方便的。另外,根据上一节,+0和-0的补码都是0000,对应的小数点是0,即0。补码只有一种表示方式,在计算机中有唯一的表示方式,这与补码不同(在补码中,0有两种二进制表示方式),所以在判断一个数是否为0,只比较一次。因为+0和-0的补码只有一种表示方式,即0000,但是原码和反码都有两种表示方式,所以补码会多??出一个1000的二进制表示方式(4位)以二进制为例),对应的十进制数-8的补码是怎么来的?前面说了负数的补码就是它的倒数加1,为什么要这样计算呢?这个计算有效吗?在十进制中,一个负数可以通过0减去一个正数,同样,在二进制中,例如:-3可以表示为0-3,也可以表示为下面的二进制减法计算,因为0(0000)是小于3(0011),根据算术运算规则,当被减数的位数小于减数时,需要从前一位借1,从前一位借1后,就变成10000。所以,上面的减法计算就变成了我们知道的,10000可以表示为1111和1的和,即10000=1111+1,所以根据上面的计算,减去3的结果就变成了(0011)从0(0000)得到1101,而1101正好是-3的补码其实上面的计算过程相当于先求反码再加1,请看下图:减去源码00113从1111,结果是反码-31100,然后加1得到-3负数的补码就是1101再看前面的介绍。一个负数的补码等于它的补码加1,是不是有点眼熟?是的,负数的补码就是这么来的。不是空穴来风的定义,而是通过上面的计算一步一步得到的,但是补码的计算方法只是它的反码加1。为什么补码适合正数的加法呢?我们还是以4位二进制为例来说明。如果有两个两个正数A和B,现在我们要证明A减去B的结果等于A加上B减去一个数的补数等于加上一个负数,所以A-B=A+(0-B)由上一节可知,(0-B)等价于(1111-B)+1。因此,A加B的补码等于A+(1111-B)+1。如果结果为R,则R=A+(1111-B)+1A+(1111-B)+1可写为A-B+(1111+1)A-B+(1111+1)可写为A-B+10000(1111+1=10000)我们以4位二进制为例,10000已经超过了4位,所以在加上10000的时候,最高位会因为溢出而被舍弃。其实此时10000就相当于0000(最高位溢出,需要舍弃)所以,上面的计算R=A+(1111-B)+1=A-B+(1111+1)=A-B+10000=A-B+0000=A-B这证明A减B等于A加B的补码总结本文介绍原码、反码和补码,着重介绍补码的由来,证明补码的可行性计算正加法
