C语言基础01-补码问题

自学计算机二级C语言,复习到补码发现忘得差不多了,重新整理了一下。算是给像曾经的我一样基础极差的童鞋们加个餐,全都用特别糙的话从根本上讲的,有基础的童鞋可以直接跳到(二)。

(一)为啥要使用补码?

很多童鞋会觉得,既然计算机内部用0000 0001表示+1,那为什么不用1000 0001表示-1,这么直观简单的方法他不香吗?为什么还要用这么神必的补码方法呢?
其实提出这个问题首先属于典型的“人类思维”,人类会觉得用1000 0001表示-1很直观,但其实在计算机看来不存在“不直观”这个问题,计算机比较“傻”,只会听人类的指示去办事,完全没有自己的思维(至少咱自家的电脑确实没有),人类怎么说它们就怎么去做;你甚至可以告诉计算机用“符号5”来代表“数字1”,虽然人类看起来会觉得特别“不直观”,但其实在计算机执行起操作来完全没差别,只要不产生歧义或者啥问题就行。
也就是说在不引起问题的情况下,计算机内部的逻辑完全是可以这么干的:
数字“1”记作符号“1”、
数字“2”记作符号“2”、
数字“3”记作符号“3”、
……;
数字“-1”记作符号“-9”、
数字“-2”记作符号“-8”、
数字“-3”记作符号“-7”、
……
用人话讲就是“指鹿为马”。

既然我们知道了上述这种更换符号的“非直观”操作在计算机内部是可行的,我们又知道计算机内部的运算是无需展示在人类面前的,所以在某些情况下,使用这种“非直观”操作可以提高计算机的效率时,我们就可以忽略其对人类观感造成的“不直观”感,大胆使用这种方法来提高计算机的效率;当计算机处理“鹿问题”比较慢,而处理“马问题”比较快的时候,我们索性就“指鹿为马”,让计算机用解决马问题的方法来解决鹿问题。

补码就是上述“非直观”操作(或成指鹿为马操作)的一种,无非是:
用符号“0000 0001”代表数字1、
用符号“0000 0002”代表数字2、
……;
用符号“1111 1111”代表数字-1、
用符号“1111 1110”代表数字-2、
……

然后上面的童鞋又会问了,那么用补码表示负数比直接改最高位到底有什么计算效率上的优势呢?
很明显,如果用直接改最高位的方式表示负数,比如用“1000 0001”表示-1,如果遇到一个运算“1+(-1)等于几?”这种问题,计算机内部就是要算“0000 0001与1000 0001的和”对吧,很明显不能直接相加,0000 0001+1000 0001=1000 0010,很明显这结果不对啊,咱知道1+(-1)=0啊,这算了个啥。也就是说如果用直接改最高位的方式表示负数,进行加减运算的时候,计算机就不能直接相加计算,得先捋到最高位看看这是个正数还是负数,然后根据正负判断进行加运算还是减运算,运算步骤直接多了好几步,这就是一个“鹿问题”。要是能想个办法给转化成步数更少的“马问题”,也就是转化成直接相加就好了对吧。
然后人们发现用补码表示负数,加减法就可以直接相加!牛逼不?不信?不信接着看。

(二)补码的原理

上面讲了所谓补码就是一种为了提高计算机运算效率而采取的特殊计数法。

原码 反码 补码
正数 二进制码,最高位取0 原码 原码
负数 二进制码,最高位取1 原码最高位不变,其余逐位取反 反码加一

举个例子:

原码 反码 补码
3 0000 0011 0000 0011 0000 0011
-3 1000 0011 1111 1100 1111 1101
-5 1000 0101 1111 1010 1111 1011

延续刚刚的问题,为什么这样操作就能把正负数加减法变成直接相加呢?
我们从反码和补码的根本定义入手分析。首先反码顾名思义,是把最高位也就是符号位以外的位逐个取反,例如:
3=(0000 0011)原=(0000 0011)反
-3=(1000 0011)原=(1111 1100)反
可见当以反码表示时,3的反码0000 0011与-3的反码1111 1100,直接相加可以得到1111 1111,同理其他相反数的反码相加也都是1111 1111,很好理解,因为符号位一定是一个0一个1,而负数后面7位都是对应正数后7位的逐项取反,也都肯定是相互0-1对应的,正数是1的位对应负数一定是0,正数是0的位对应负数一定是1,所以首位相加是1、后7位相加也分别是1,所以:所有相反数的反码相加都是1111 1111。很明显这个时候仍然是不能直接相加的,因为我们知道相反数相加和应该为0,所以我们需要在反码表示的基础上再进一步修改
我们又知道1111 11111+0000 0001=1 0000 0000,在8位int数中,舍弃溢出位的1,就变成了0000 0000,也就是说所有相反数的反码相加后再加一个1,在计算机的int数识别机制中就可以得到0000 0000;而我们要保证计算机运算过程中相反数相加和为0,也就是说需要在反码表示的基础上,在正数的反码上加1,或在负数的反码上加1;既然两种选择都可以,那我们索性不如姑且保持正数表示的直观性,故而在负数的反码上加1。根据以上的推理,我们可以知道,用“正数保持原码表示,负数用反码加1的形式表示”这种操作,一对相反数相加后舍弃溢出位的1即可得到0000 0000,这其实就是将负数用1 0000 0000减去对应正数后得到的结果来表示;又因为看起来“像是”用0000 0000减去对应正数后得到的结果来表示,使用这种方法表示的负数就像是与对应正数“互补”,所以这种表示方法就称作“补码”。补码的原理至此解释完毕。
这同时也解决一些初学童鞋的一个问题:为什么正数的反码和补码是原码本身?——那是因为,从上述推理看得出为了实现我们“直接相加”的目标,只需要将负数进行补码操作,而正数全程无需变化,你甚至可以说正数根本不需要反码和补码;也就是说并非是“正数的反码补码是原码本身”,而是“没必要特地让正数的反码补码与原码不同”。

你可能感兴趣的:(补码)