也许有人会说,这个问题好傻呀!我们一开始接触计算机它就是这样个定义呀。如果你有同样的想法,其实很正常,因为我以前也是这样的想法。但是,我得提醒大家,任何事都不要习以为常,因为这样所有问题和有价值的来龙去脉可能就会被忽略掉。
1 时钟引发的问题
在讲这个话题前,我们先看看现实生活中一个例子,比如现在时间是上午11点,这时有个需求,说这个时钟(那种12小时制的壁挂式时钟)不准,要调整到上午9点,我们是不是有两种方案。
第一种,将时间往前转2小时,表现为11 - 2 = 9 时;
第二种方案,将时间往后转10个小时,表现为 11 + 10 = 11 + (12 - 2) = 11 - 2 = 9 时。
为什么会想到加上12,因为任何时间点,加上12,又回到了原来的时间点上,所以加12可以等于没有做任何操作。
由上面的例子可以看出,减2和加10可以达到一样的效果。我们可以得出以下结论:
任意两个数之和如果为模数,那么加上其中一个数就等于减去另一个数。
对于这个常见的生活现象,我们习以为常,并不会觉得奇怪。人有时就是这样,对常见的现象容易产生它就是常识的这种误解。哈哈,又扯远了...
接下来我们看看计算机中,如果运用这种循环往复的计数方式。
2 假想八位的计算机
先看一张图,打了一个比方,如果计算机像时钟一样标刻度,应该就如同下面这个样子。
在无符号中,溢出点在7,7+1会溢出。在有符号计算机中,溢出点在3,3+1会溢出,溢出后的数为三位计算机中所能表示的最小数,也就是-4。
首先我们要明确一点:计算机做加法更加方便快捷。我们来假设现在计算机中,有3位存储空间,如果无符号也就是2^3=8,也就是0~7八个数。由上面的时钟的例子我们很容易知道,7+1会溢出,导致其值变成0。
我们具体来看看是什么原因:(背景为无符号3位的计算机)
7的二进制表现形式:111,加1,就是111 + 001 = 1000;因为是3位的计算机,所以第四位溢出被抛弃掉。最终运算结果为000也就是0。
我们再来看看减法,小时候学习减法时,老师都会教,如果减数同位大于被减数,那么就会像高位借。从这个角度来说,减法(相比于加法)其实是更难的。而减法其实可以看作是加上了一个带符号的数,从这个角度来理解,我们一起来看看一个例子。
1)十进制 6 - 1
2)二进制 110 - 001
3)相当于 110 + 1000 - 001 (三位的计算机,任何数加上1000都不会改变,因为1000为8位计算机的模,就比如上面时钟中任何数加上12时都还是本身一样)
4)相当于 110 + (1 + 111 - 001)
5)相当于 110 + 111
6)结果为 1101 (第四位溢出,结果位101 = 5)
我们仔细看看上面的过程,定会得出如下结论:
如果把6 - 1看作6 +(-1),那么你会发现,6 +(-1)就等于110 + 111,而111 其实就是 001的反码110 + 1,也就是补码的定义。
到这里,大家应该就明白了,为了计算简单,计算机会把减法化成加法,而溢出却成了一个又好又巧妙的办法。
3 总结
在以12为模的时钟例子中,-2和+10是同一个结果,因为这两者刚好相差模数个刻度,也就是12时,所以由此可以看出,任何相差模数刻度的两个计算,产生的效果是一样的。比如:-3和+9,-4和+8等等。
而补码定义中,取反是什么,加1又会是什么。取反运算后的两个数相加就是这类数当中的最大值。加1后,变成了互补的两个数,运算就可以相互转换。
操作 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
二进制 | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
取反 | 111 | 110 | 101 | 100 | 011 | 010 | 001 | 000 |
十进制 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
加一 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
世间万物,循环往复,周而复始。也许悲伤的尽头就是快乐。