本文要讨论的话题是:补码。
在介绍补码前,先介绍一下,什么是原码,什么是反码。
在计算机里,是用二进制来保存整数的,假设我们用1个字节(1字节=8bit)来表示整数(实际在多数编程语言里都是4个字节),规定最高位为符号位,其余位为数值位。
正整数10的二进制为00001010,这个就是原码。
数值位取反后得到的二进制,就是反码,取反就是将0变成1,1变成0。
有基础的同学肯定知道,计算机是用补码表示负数的,补码=反码+1,举个例子,-10的原码为10001010,-10的反码为11110101,-10的补码为11110110。
那么,为什么要用补码表示负数,为什么补码一定是反码+1呢?
为什么要用补码表示负数
第一个问题,为什么要用补码表示负数,用原码不行吗,下面我们来探讨一下。
计算机里只有加法运算,没有减法运算,如果是10-5,那就相当于10+(-5),因此,如果10和-5都用原码表示,那么相加的过程如下。
结果是-15,那肯定不对啊,为什么是-15呢,从图中也可以看得出来,10与-5相加后结果的数值位就是10+5。
因此计算机为了保证结果的正确性,需要用其它的方式来表示负数,这种方式也就是补码。
补码是怎么推导出来的,为什么一定是反码+1?
下面来探讨一下第二个问题。
前面的计算结果是错误的,究其原因还是计算机做不了减法,如果是做加法的话,就需要用到负数的补码,假设我们现在不知道补码是多少,下面就来一步步推出补码。
比如10+(-5),我们现在不知道-5的补码是多少,但是我们可以换一个角度思考,-5和相反数5相加是0,如何让他们相加是0呢,我们来画一下推导过程。
第0位:这里需要填1才能保证结果是0,并向上进一位。
第1位:这里需要填1才能保证结果是0,并向上进一位。
第2位:这里需要填0才能保证结果是0,并向上进一位。
第3位:这里需要填1才能保证结果是0,并向上进一位。
第4位:这里需要填1才能保证结果是0,并向上进一位。
第5位:这里需要填1才能保证结果是0,并向上进一位。
第6位:这里需要填1才能保证结果是0,并向上进一位。
第7位:由于最高位是符号位,因此比较特殊,这里有两种原因需要让它填1:
- 负数的符号位为1
- 需要填1才能保证结果是0,由于是最高位因为不用再向高位进一
推导过程结束,最终-5的补码为11111011,将它与10相加,结果就是正确的5。
那么上述的推导过程有什么规律没?细心的同学可能发现了,规律肯定是有的。
其实-5的5除符号位外,数值位的二进制码是一模一样的,也就是说5的每一个二进制位就是-5的原码。
由5+(-5)的计算过程发现的一般性规律(填充位即前面图中的?):
- 当低n位(这里n需要视情况而定,低位有几个连续的0,则n就是几)的原码都是0时,则都填充0,这样才能保证低n位的结果是0;
- n+1位为1时,则该位填充1才能保证为0,并向上进一位。
- 由于n+1位向上进了一位,则n+2位后的每一位,原码+填充位相加的结果只能是1(如果原码是0,则填充1,原码是1,则填充0),这样才能保证每一位都进1,并且原码+填充位+进位的最终结果是0。
由此可见,需要满足如下条件才能称叫做补码:
- n+2位后的每一位,原码与填充位相反,即n+2位后的每一位,取反就能得到填充位;
- 而n+1及低n位的原码类似于10…0,填充位类似于10…0,原码取反类似于01…1,原码取反+1就可以得到填充位;
因此整体的关系是:原码取反+1就能得到填充位,这就是补码!
另外,还可以换一种角度得到补码的规律,那就是除符号位外的所有位都取反,保证每一位相加结果是1,然后最低位再补个1,保证每一位的结果变成了0,并都向上进一位。这样也能得到取反+1的规律。
后续
补码含义:
- 对于时钟,一个周期是12个小时,因此从4点到1点,可以倒退3小时,也可以前进9小时。
- 而对于负数-10(类似于-3),可以转化为+118(类似于+9),只不过这里的周期是128(一个字节)。
- 那么+118就是-10的补码。可以看出,补码与原码的绝对值相加,就是一个周期。
- -10的原码与反码相加,每一位都是1,因此和是127,再加个1,就是128。
如何根据-10的补码求其值?
- 由于补码是118,因此符号位代表-1即可,即-1*2^7+118=10。
补码运算:
- 符号位也参与运算,符号位产生的进位丢掉,结果的符号由运算得出,补码的运算结果仍是补码。
- 如何求证呢?试着画出-10+9和-10+11的二进制运算过程就知道了。