目录

在开始漫游之前,一个良好规划的行程当然是必不可少的。

在这篇关于数字的游记中,我们会先从数字的起源谈起——这部分应该算是唯一的题外话了,但足够有趣。在数字出现之后,一些早期的数字系统随之而来,然后人类就开始了在数字系统上几千年的摸爬滚打,历经千难万苦,这才用上了位置相关的阿拉伯数字。当理解位置相关的数字系统之后,我们便做好了所有的准备,可以朝二进制进发了…

数字的起源

在谈论数字之前,我们先来谈一谈什么是抽象。说起来倒也有趣,我们大多数人都知道世界上有这么个词,但对这个词我们大多数人却又都说不出个子午卯酉来。如果抛开“抽象”在哲学和艺术领域的含义,并且将其视为一个动词的话(可以理解为我在偷懒),这个词的定义其实还是很好给出的,比如我们可以参考维基百科的定义:

Conceptual abstractions may be formed by filtering the information content of a concept or an observable phenomenon, selecting only the aspects which are relevant for a particular subjectively valued purpose.

说人话就是,抽象指我们带着某个目的对一个概念或一种现象进行过滤,然后我们扔掉与目的无关的细节和信息,只保留与目的相关的信息,比如把你踢的足球简化成一个球,这便是一种抽象。

其实除了你的足球,数字才是生活中我们接触最多的抽象。当我们看到一个数字比如 “3” 的时候,我们马上就可以联想到三个柚子或三头狮子什么的。但我们必须要接受一个事实,那就是原始人可绝对不会这么想。在他们看来三个可爱的柚子和三头狮子之间一点儿关系都没有。后来过了大概几千年,虽然数字仍然没有出现,但我们人类已经形成了文明。比如有一个马戏团驯养了三头狮子,马戏团主人可能会请一个画师画三头栩栩如生的狮子,以便记录自己的财产。后来,马戏团主人琢磨起来:我为啥非得把狮子画这么像?能看出来是狮子就行了。于是马戏团主人把画师解雇了,自己画了三张狮子的简笔画。再后来,马戏团主人画着画着画烦了,又开始琢磨起来:我干嘛非得把狮子画出来?然后马戏团主人在纸上画了三条杠,代表自己的三头狮子。整个过程类似于下面这样:

假如马戏团主人家里恰好又有三个柚子的话,他可能就会有所启发:也许我同样可以用三条杠代表我拥有的柚子。于是,马戏团主人跨出了原始人没有跨出的那一步,数字诞生了。

如果历史上真有这么一位马戏团团长,我们一定得给他颁个奖。

我们可以看到,数字的诞生其实就类似于这种不断抽象的过程,我们为了计数,一步一步地抛弃一些细节,最终抽象出三个柚子和三头狮子的共同点:数字 “3”。

我们的十个数字

可是问题来了,随着马戏团的世界巡演,马戏团主人挣的钱也越来越多,为了演出需要马戏团主人又购买了二十条可爱的小狗(二十头狮子太可怕了),于是他在自己的本子上画了二十条杠。这时候马戏团主人心里已经有隐隐的不安了:假如我以后再买一百条小狗难不成还得画一百条杠杠?随着这个想法的闪现,离世界上第一个数字系统的诞生也不远了。

所有早期的数字系统中,只有罗马数字流传到了现在,按我们的文明发展顺序,我们当然要首先简单介绍(吐槽)一下罗马数字。

罗马数字是位置无关的数字系统,也就是说不管数字出现在哪个位置它的值都是相同的,假如理解上有困难的话,不妨想一想我们阿拉伯数字系统中的 10 和 10000 中的 1 代表的值是否一样。假如有一个罗马数字 VII,V 代表 5,I 代表 1,但 V 出现在右起第三位并不代表 500, 事实上 V 不管出现在哪个位置都代表 5,所以将 VII 转换成阿拉伯数字只要直接相加就行了,5+1+1 也就是 7。更复杂一点的数字如 MCMXCVII,咱们一起费点时间看看怎么读出来(叹气)。数字里有一个 M,代表 1000,接下来是 C 后跟一个 M,因为小值后跟一个大值,所以合起来是 1000-100 的意思,再接下来是一个 XC,跟 CM 的规则类似,是 100-10 的意思。最后是 VII,因为大值后跟小值,所以直接相加,即 5+1+1。好了,我们算了半天,终于成功的念出了 1997(马上忘掉这一段吧)。至于罗马数字的加减乘除我们就不再讨论了,简单的加减运算还算方便,可乘除运算——可以这么说,假如我们如今仍使用罗马数字的话,那么两个罗马数字的乘除问题有成为高考数学最后一道大题的资格。

正如我们现代人无法想象罗马人究竟是怎么忍受罗马数字一样,罗马人也完全无法理解我们现代人竟然可以使用另外一种完全不同的方法计数,也就是后来印度人发明的位置相关的数字系统——印度-阿拉伯数字系统

这个数字系统不同于早期数字系统的地方在于

  • 阿拉伯数字系统是位置相关的,也就是说一个数字的位置不同,其代表的数量也不同。参考前面提到过的例子,10 和 10000 中都有一个 1,可我们知道 10000 要远远大于 10。
  • 早期的数字系统中有一点是阿拉伯数字系统所没有的,那就是专门表示 10 的符号,比如 iPhone X 中的 X。
  • 可阿拉伯数字系统同样有一个几乎所有早期数字系统都没有的符号,而这恰恰是一个比表示 10 的符号还重要得多的符号——数字 0。

当我们看到一个阿拉伯数字 1234 时,我们可以把它的结构写成这样:

$$\footnotesize 1234=1000+200+30+4$$

也可以写成这样:

$$\footnotesize 1234=4*1000+2*100+3*10+4*1$$

或者这样:

$$\footnotesize 1234=4*10^3+2*10^2+3*10^1+4*10^0$$

我们可以从这些形式中体会一下位置相关的数字系统,或称位值计数法的特点。

接下来我们来重新感受一下阿拉伯数字那些早就被我们习惯性忽略,但比起早期数字系统却无比闪光的地方。我们都知道,3 加 4 等于 7 ,类似的,30 加 40 等于 70,300 加 400 等于 700,330 加 440 等于 700 加 70。也就是说任何长度的阿拉伯数字相加时,只要根据一种方法将问题分成几步即可,而且每个步骤也就是把两个个位数字相加而已。乘法虽然复杂了一点点但也同样类似,我们仍然只需要把问题分解成几步,做加法和个位数的乘法即可,更复杂的乘除计算我们也可以求助于竖式解决,这样我们就把乘除的难度从“高考数学最后一道大题”一下子降到了小学生课堂。

此外我们需要认识到,数字 0 是阿拉伯数字系统中的精要所在,我们前面已经讨论过数字的抽象过程,如果仔细思考,我们会发现要想完成 0 这个概念的抽象,其实并没有想象中那么的简单。再考虑到对人类数学发展的巨大贡献,小小一个 0 无疑是数字和数学史上最重要的发明之一。0 符号支持位值计数法,因此我们可以在使用阿拉伯数字时将 0 用作占位符,从而区分 25、205、250 之类的数字;0 的出现同样使我们再也不需要表示十和其倍数的专门符号,想想罗马数字吧,为了表示超过 1000 的数,罗马数字在中世纪专门增补了 $$\footnotesize \overline{V}$$,$$\footnotesize \overline{X}$$,$$\footnotesize \overline{L}$$,$$\footnotesize \overline{C}$$,$$\footnotesize \overline{D}$$,$$\footnotesize \overline{M}$$ 等符号来表示 $$\footnotesize V$$,$$\footnotesize X$$,$$\footnotesize L$$,$$\footnotesize C$$,$$\footnotesize D$$,$$\footnotesize M$$ 的 1000 倍,要想表示更大的数字,还得继续加杠——这不就回到马戏团主人的时代了嘛!

时至今日我们已经对 0 和阿拉伯数字系统的使用习以为常,仿佛它们本来就存在在这个世界上,而不是被人为设计出来一样。当意识到这些时,其实感觉还挺有趣的。不知道几千年后会不会出现另外一种数字系统,让使用十进制的我们显得和罗马人一样滑稽。

介绍完阿拉伯数字系统和 0,我们似乎还需要注意一下另外一个由阿拉伯数字系统的位置相关特性造就的,且同样很特殊的存在:数字 10。

关于10

10 对我们人类而言无疑是个非常重要的数字,因为我们早已习惯了以 10 为基数的数字系统,也就是十进制。而世界各地的古人不约而同地选择了 10 作为基数,或许和我们的手指非常便于计数,手指头的数量又恰好是 10 有关。

现在当我们看到 10 的话,我们可能会立刻想到下面这么多头狮子:

可是,我们一定要认识到一点,10 之所以代表上面这么多头狮子,唯一的原因就是我们考虑到自己的手指头数量所以采用了十进制而已。 虽然这么做很适合人类,可是这对 Tom 和 Jerry 就很不公平了,因为这对冤家都只有八个指头。

根据我们人类的经验,我们会发现对 Tom 和 Jerry 来说八进制才是最适合他们的。所以本着人猫鼠平等的态度,我们给 Tom 和 Jerry 专门建立一个以 8 为基础的数字系统。在这个系统里,符号“9”是不被需要的,而且你仔细一想会发现我们甚至连符号“8”都不需要。因为我们十进制中没有专门表示数字 10 的符号,当然八进制中也不需要专门表示数字 8 的符号。

在十进制中,我们在计数时分别使用 1,2,3,4,5,6,7,8,9,之后是 10,而八进制中情况略有不同,我们在数完 1,2,3,4,5,6,7 后,八进制中的符号就已经被用完了,所以 7 接下来同样是 10,但是注意,此时进位已经改变,所以这里的 10 代表的其实是数量“八”。

所以我们可以思考一下 10 的本质:10 并不是发明出来专门指代数量“十”的,10 的产生是由于当各个进位制中的个位数字符号使用完毕后,个位置 0,十位进 1 而得(其实除了 10 之外,20,30,200,300 等数字也是同样的道理,只不过规则需要稍微扩展一下,变成低位数字使用完毕后,低位置 0,高位进 1)。

为了避免混淆,我们可以将非十进制的“10”读作“一零”,当书写其他进制的数字时,我们可以加上一个下标来区别不同的进制,比如八进制数 123 就可以记作 $$\footnotesize 123_{8}$$。

假如给你一个数字 $$\footnotesize 1234_{8}$$,我们可以尝试一下用类似之前我们拆分十进制数字 1234 的方法写出八进制数字 $$\footnotesize 1234_{8}$$ 的不同形式,比如:

$$\footnotesize 1234_{8}=4*1000_{8}+2*100_{8}+3*10_{8}+4*1$$

或者:

$$\footnotesize 1234_{8}=4*512_{10}+2*64_{10}+3*8_{10}+4*1$$

或者:

$$\footnotesize 1234_{8}=4*(8_{10})^3+2*(8_{10})^2+3*(8_{10})^1+4*(8_{10})^0$$

试着去理解这三个式子(可以想想之前十进制数字的不同形式,再想想 10 在不同进制的含义)。当你理解之后,那么祝贺你,所有的非十进制在你眼里就应该都不再有什么神秘面纱了。另外还要祝贺你顺便解锁非十进制转十进制技能,想必聪明如你们肯定会发现,后两个式子便是其它进制转十进制公式!

有了理解之后,公式就变得似乎没那么讨厌了。

哆啦A梦

接下来我们步子要迈的大一点,我们说过人猫鼠平等,机器猫也是猫,咱们同样要为哆啦A梦先生设计一个方便的数字系统,鉴于哆啦A梦只有两个…额…指头?所以我们选择了 2 作为基数,也就是(终于写到这里了的)二进制!

想必作为机器猫,哆啦A梦绝对会喜欢二进制的。

现在我们只有两个数字了,0 与 1。虽然经过之前八进制的洗礼,我们对不同进制早已一法通万法通了,可是我们还是得花时间习惯一下二进制,因为为二进制数字用完的太快了……未完待续………………………

先等等……等我先活过复习周考试周再说。

接下来我们会深入一些。

精巧的补码

可以想象,加减乘除是计算机非常基础的运算了,这里的基础意思是我们需要专门设计逻辑电路来实现这样的运算,而不是利用已有的电路。假如想要实现一个加法电路的话,我们先回忆一下小学数学老师教过我们的知识,比如加法始终是从两个加数的最右列向最左列进行计算,再比如某一列有进位的话,我们需要把进位加到下一列中。而在计算机中,我们使用异或门、与门和或门便可以组成一个加法器!

其实我们只需要用一些继电器和电灯泡就可以构建出所需要的逻辑门,从而制作出一个货真价实的加法器。当然我们不用专门去买材料,用演草纸或者大脑就行。按这篇文章写到现在专注于原理的风格,我本来是应该写一写的,不过想了想还是算了——虽然在脑子里用灯泡和继电器成功构建一个加法器会让你体会到巨大的成就感(尽管很短暂),但你构建的过程会比较痛苦(我写起来更痛苦…)。再者,大家也知道我们程序员是无需太过关心底层细节的,虽然了解一些底层细节有助于我们编程能力的提高,但加法器……这也太底层了。所以,努力按下好奇心吧,来看一些比加法器更有趣的东西。

当我们假装成功设计了一个加法电路后,针对减法运算我们似乎理所当然的也要设计一个减法电路出来。可当我们回忆一下减法规则后我们发现,减法中没有进位,但却有一个完全不同而且麻烦无比的东西:借位!

怎么办?咬着牙上之前先想想我们路桥人是怎么修路的,遇到大山挖不起我还绕不起吗!

倒不是说我们没有愚公移山的精神,实际上这是一个很正确的选择。不难想象,最底层增加的哪怕一丁点开销传递到上层都会被放大无数倍,我们当然要以最小的开销去实现最基础的运算。所以我们用了一种很聪明的办法,使减法运算直接利用加法电路就能完成!

化减为加

现在,假设我们有一个只有一位数字的计数系统(不是数字系统,就是一个有计数范围的计数器一样的东西),然后把存在于脑海中的那条无限长的数轴截出长度为 10 的一小段弯成一个环,就像时钟那样。所以当你从 0 数到 9 后继续数,你就又回到了 0(0 和 10 其实是重合的,想想十二点和零点)。这时我们称 10 是这个计数系统的

  • 模指的是一个计数系统的计数范围,比如时钟的计数范围是 0~11,所以模 = 12,我们也可以自己脑海里构建一个 0 到 99 的计数器,那么模就是 100。我们可以看到,模实质上是计数系统刚好产生溢出时的量,所以在计数系统内永远都取不到模的值。

好,我们来看一下如何变减为加。首先先想象一个时钟,假设当前时针指向 10 点,而准确时间是 6 点,调整时间可有以下两种拨法:一种是倒拨 4 小时即:10 – 4 = 6;另一种是顺拨 8 小时即:10 + 8 = 12 + 6 = 6。在以 12 为模的系统中,加 8 和减 4 效果是一样的,因此凡是减 4 运算,都可以用加 8 来代替。对“模”而言,8 和 4 互为补数。不难想象以 12 模的系统中,11 和 1,10 和 2,9 和 3,7 和 5,6 和 6 都有这个特性,共同的特点是两者相加等于模。

时钟模型只是块敲门砖,因为时钟上的数字毕竟是刻在表盘上的,是时针在动又不是数字在动,所以我们可能会忽略掉关于进位的一点小细节。由于十进制还是更易于理解,我们还是使用最开始建立的那个模为 10 的计数系统。现在,想象面前有一个小窗口,这个窗口仅仅只能显示一个数字,所以当窗口里的数字加上一个数字产生进位的话,问题就来了,因为窗口只能显示一个数字,所以简单粗暴地,进位会被直接丢弃,窗口中只会留下个位的数字。前面也提到过 0 和 10 其实是重合的,现在我们知道这只是因为 10 的十位被丢弃后变成 0 而已。好了,这样的模型才更贴近计算机一点。

或许你认为已经结束了,但我们暂时还不能放松下来,事实上我们还有几个很重要的问题没有解决,上面的模型毕竟也只是也贴近而已。

符号位

如题,我们要意识到,计算机可并没有符号这个概念………………………………….未完待续

还是那句话,等我先活过复习周考试周再说。不过忍不住了,先贴上来吧。