一开始我也不理解,为什么,补码要这么算,突然的顿悟也是在写下这篇文章的晚上,尽管很多人都跟我说:“你不要管为什么,你只需要记住就可以了。”但是人类的求知欲是很强大的,甚至可以让人看到海对面的敌人(巨人梗)。
其实最根本的原因就是一件事:计算机一次能够处理的数据长度是有限的。
根本原因就这么简单,下面我们来说为什么是这个原因。
现在,我们不站在二进制的角度去思考问题,转而把一个个二进制数字想成是十进制数字,就拿最简单的一个字节来说吧,那么我们能够表达的数字范围就是0-255。很容易写成一个数轴的形式不是吗?最左边就是0,最右边就是255。
现在,我们引入一个我自创的概念,我喜欢称它为数轮。我们将这个自创的数轴弯成一个圈,使得首尾相连,于是乎我们便得到了数轮,这个坐标轴便从一维的线段,跳脱出来,变成了二维的图像,任何事物在高维看起来总是非常简单,不是吗?
下面我们来进行一些连接的工作,将这个轮子和你日常所见到的现象连接起来,为了方便后续讨论,我们这里将这个轮子顺时针规定为原来数轴的正方向,正上方为0点,即如果我们从0沿着顺时针出发,数字会不断增大,最终我们会回到0点。
我们要知道,这个轮子上的每一个点,对应的都是原来二进制储存器当中的一个个数字,也就是说,每一个数字,都有一个二进制储存状态与之对应。
当我们从255,走到0的时候,二进制储存内部发生了什么呢?
11111111->00000000
溢出了,高位进1,但是不要紧,我们通常会把它丢弃。
发现了吗?我们只关心最低8位数字,换言之所有8位之外的结果都会被我们全部抛弃,我们本字节内部的数字世界只有0-255。
当我们从255,走到1的时候,二进制储存内部发生了什么呢?
11111111->00000001
越过0,到达了1,至此我们获得了一个伟大的定律,在这个小小的8位储存器中,所有的溢出不过都是沿着正向的向前行进,越过0点罢了。
更进一步,我们还得到了一个比较有趣的结论:溢出也可以用来到达0点。
于是乎我们就可以利用这个特性,来做一些有趣的事情,例如我要计算5减去5,我倒着走是一种路径,那么为什么我不能正着走,重新回到0点呢?
对应到数轮上,其实不就是在往前走251步吗?那在这个小小的8位存储之中,5+251和5-5的效果不就是等价的吗?
对,就是等价的,尽管考虑到进位溢出还会有额外的操作,但是我们暂时先只聚焦与这8位上,以方便我们的讨论。
现在我们来看看:
251的二进制:11111011
5的二进制: 00000101
当它们相加,产生的256的二进制就是:1,00000000
看,这个256在向我们打招呼,它在说:去0点。
同理,我们可以计算任何数字,二进制只取最后8位的情况下,数字都会落在数轮之内,也就是我们自动完成了模256的运算。
说到这里,我们就可以绕回题目:补码的意义是什么呢?
上面我们已经证明了,对于这个轮子上的任意一个数字位置,我们从任意点出发,总是存在两条路径可以到达该点。那么我们自然而然也就可以得出:补码实际上就是替代减法的另一条路。
就像5减去5,我们没有减法电路,但是我们可以用补码,走另一条路,同样可以到达0点。于是乎所有的减法都变成了加法,所有的计算都在此刻被瞬间简化。
至于补码为什么要取反再加一,其实你已经可以自己推导出来了,不过我们这里还是可以推导一下,-5的补码是多少。
1、-5源码是:10000101
2、-5反码是:11111010
5源码是: 00000101
3、-5补码是:11111011,即251:11111011
发现了吗?-5的反码和5的源码相加等于255,下一步就是溢出,即我们需要运算到的地方,如果你画一下数轮,你会发现,-5的补码所指的位置,恰好就是-5的位置。
上述讨论并不严谨,是有漏洞的,不过作为解释补码意义的讲解,不需要特别挑出瑕疵。
至此,补码的意义也终于为我们所理解:补码其实就是用加法替代减法的工具。
但是补码需要一个根本条件才能成立,那即是:储存空间有限,只能表示有限的数字。否则补码其实是没有用的,例如给你无限长的二维储存空间,那么你的数字上限是无穷大,仅仅是计算反码这一步,你就做不到,因为你穷尽一生,也没办法达到无限。(人类还是有局限的啊!JOJO!)