小池有话说

进制、负数和补码

2013-08-24

人类为什么使用 10 进制呢?据说是因为人类有 10 根手指。电脑使用二进制,原因大概是二相的电器元件容易制造。

不管是人类的 10 进制还是电脑的 2 进制,归根结底都是为了表达数字。数字是独立于进制的,不管进制是什么,数字总还是那个数字。

很久很久以前,人类对于10以上的数字是没有概念的。大家都是就着自己的手指头数数,数到10就达到了极限。可是有个天才发明了一种做法,让人类的数数能力获得了质的飞升。他是这么做的:数完了自己的手指头,就在自己的身前放一颗小石子。然后让自己的手指头“清零”,继续数数。数完所有东西后,再回来数数小石子。于是,人类发明了二位数。 二位数发明之后,三位数也不远了。

人类历史上不仅出现了 10 进制,还出现了 20 进制。想必,发明了两位数的天才出现之前,出现过另一个天才,想到了用脚趾头数数。

二进制表达数字也和 10 进制类似,你把计算机想象成一个只有两个手指的怪人,计算机数到 2 ,就在自己身前放一颗小石子。

下图中,左边是十进制,右边是二进制。二位的两进制只能表达那么多个数。

1     1
2    10
3    11

就像老师经常说的,十进制是 逢十进一 ,二进制是 逢二进一 。只要照着这个规则,我们可以将上面的表一直对应下去。

根据进制表达数的本质,我们可以写下如下的 python 代码,对任意进制的数字求值。

def value_base(reprs, base):
    base = 10
    lst = list(str(reprs))
    lst.reverse()
    val = 0
    b = 1
    for i in lst:
        val += int(i) * b
        b *= base
    return val

二进制表达负数。人类用-/+来表达正负的概念,二进制中用最高位 0 / 1 来表达正负的概念。 0 就是正数, 1 就是负数。

为什么负数要用补码表示?

在现代计算机中,负数是用补码表示的。

比如, 5 的二进制表示形式是 101 。如果字长是 8 ,就是 00000101 。表示负数的时候,只需将最高位逆转。那么二进制的 -5 可以用 10000101 来表示。这本来没什么问题,可是考虑到计算部件的成本,人们用了另外一种表示方式。

如果我们用上面的方法表示正负数,在计算机中,加法电路和减法电路就是两套不同的电路。 但人们希望,加法和减法可用统一用同一套电路(加法电路),以便减少成本。令人吃惊的是,这竟然可以通过编码来做到。

为什么补码是取反加一

至少,相反数相加必须得 0 。 5 和什么数相加能够得 0 呢?那么这个数不就是 -5 了?

00000101
xxxxxxxx
--------
00000000

显然这个等式是不可能成立的。我们让结果得到一个进位,一切就变得合理了。

 00000101
 xxxxxxxx
 --------
100000000

计算机在做加法的时候,如果进位超出了范围,会被舍去。接下来,求出 x 是小学生都会做的题目了。一个巧妙的思路是这样的,先求出下式中的 y ,然后让 y 加 1 ,即得 x 。

00000101
yyyyyyyy
--------
11111111

求y的过程非常简单,正是 取反操作 。取反操作就是 0 变 1 , 1 变 0 。

这就是为什么补码要取反加一的由来。

这也可以解释为什么补码是自反的。所谓自反就是补码再次取反加一就得到原码,因为从 5 找 -5 和从 -5 找 5 是一个道理。

补码 之所以叫做补码,就是因为它和原码相加,会让结果归零。它和原码天生是一对。