数制(Number Systems)与进制(Bases)

像二进制和十六进制这样的进制系统乍看起来有点奇怪。关键在于理解不同的系统在“满”时如何像里程表一样“进位”。十进制系统(Base 10)在达到10个项目时“进位”,创建新的一位。我们等待60秒后才“进位”到新的一分钟。十六进制和二进制类似,但分别在每16个和每2个项目时“进位”。

尝试在这里将数字转换为十六进制和二进制:

很久以前:一元数

很久以前,我们还没有进制系统!那时无论上坡下坡,都要穿过积雪和酷热。当你想数一个时,你会写:

l

当你要数5时,你会写

lllll

显然,1 + 5 = 6

l + lllll = llllll

这是最简单的计数方式。

罗马人登场

在罗马数字中,二是一的两倍。三是一的三倍:

one = I
two = II
three = III

然而,他们决定可以做得比在沙子上画线的古老传统更好。对于五,我们可以用V来表示lllll,得到类似

l + V = Vl

不错吧?当然,你还可以使用更多符号(L、C、M等)。

关键点是,V和lllll是编码数字5的两种方式。

给每个数字起名字

另一个突破是认识到 每个数字 可以是一个独立的概念。与其将三表示为一串一,不如给它自己的符号:“3”。从一到九都这样做,你就得到了这些符号:

1 2 3 4 5 6 7 8 9

罗马数字已经很接近了,非常接近,但只给了5、10、50、100、1000等独特的符号。

利用位置

显然,你无法给 每一个 每个数字都赋予自己的符号,因为数量太多了。

但请注意罗马数字的一个洞见:它们使用 位置 符号的组合来表示意义。

IV 表示“从5中减去1”,

而 VI 表示“在5上加1”。

在我们的数字系统中,我们以类似的方式使用位置。我们 始终相加 只加不减,每个位置都是前一个位置的10倍。

所以,35 表示“加 3*105*1”,而 456 表示 4*100 + 5*10 + 6*1。这种“按位十进制”的设定就是我们今天使用的印度-阿拉伯数字系统。

我们选择十进制

为什么我们每次选择乘以10?很可能因为我们有10根手指。

有一点需要认识到:我们需要足够的数字来“填充”,直到达到下一个数字。让我演示一下。

如果我们想每10个数就滚动一次里程表,就需要1到9这些数字符号;我们还没到10。想象数字缓慢向上递增——在什么时候你会翻到下一个单位并从零开始?

引入零

当我们达到10时会发生什么?我们如何表示我们想要恰好一个“十”而“个位”列没有东西?

我们使用零,那个不存在的数字。零是一个相当了不起的概念,它是一个占位符,一个空白,一个空位,还有很多其他含义。可以说,零是有史以来最伟大的发明之一。

零使我们能够拥有一个空的占位符,这是罗马数字所没有的。看看没有零时他们的数字是多么笨拙。

乔治·奥威尔著名小说《1984》将写作“MCMLXXXIV”!读起来朗朗上口,不是吗? :)

考虑其他进制

记住我们 选择 每十次就滚动里程表一次。我们的计数如下:

1
2
3
4
5
6
7
8
9 (uh oh, I’m getting full!)
10 (ticked over – start a new digit)

如果我们像对待秒和分钟那样,每60次才进位一次呢?

1 second
2
3
4
5
…
58
59
1:00 (60 seconds aka 1 minute. We’ve started a new digit.)

到目前为止没问题,对吧?注意,我们使用冒号(:)表示进入了一个新的“位”。在十进制中,每个数字可以独立存在。

尝试十六进制

如果我们想要十六进制,可以类似地做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 (we’re getting full)
1:00 (16 – we’ve started a new digit)

然而,我们不想用冒号记法来写十六进制数(尽管我们可以)。我们更愿意为10-15创造单独的符号,这样我们就可以像平常一样直接写数字了。我们已经用完数字(1-9已用,0作为占位符),所以需要其他符号。我们可以用一些波浪线或其他形状,但惯例是用字母,罗马风格。就像5变成V,程序员用字母A-F来凑足16个数字。也就是说:


1
2
3
4
5
6
7
8
9
A (10 – we’re using the symbol “A”)
B (11)
C (12)
D (13)
E (14)
F (15 – uh oh, we’re getting full)
10 (16 – we start a new digit)

啊哈!现在每个“位”可以用一个数字表示,而且我们知道10实际上意味着“已进位到16”一次。

20意味着进位到16两次(32)。

25意味着进位到16两次(得到32)再加额外的5。总共是32+5=37。

快速回顾

跟得上吗?这很酷,对吧?我们可以在任意进制下计数。另外注意,十六进制更“节省空间”,比如数字11可以写成单个数字B。

十六进制与十进制其实没多大区别,只是更大才进位。

奇妙的二进制世界

我们已经见过很多进制,从最简单的的一元,到繁琐的罗马数字,稳定的十进制和紧凑的十六进制。

二进制好在哪里?本着简单的精神,它是具有“进位”概念的最简单的数字系统。一元,只是写1,11,111……无穷无尽。二进制有两种选择(1和0),如下:


1: 1
2: 10 (we’re full – tick over)
3: 11
4: 100 (we’re full again – tick over)
5: 101
6: 110
7: 111
8: 1000 (tick over again)
…

等等。

因为二进制非常简单,所以很容易用硬件实现。你只需要能开关的东西(表示1和0),而不是需要10种状态的东西(表示十进制)。

因为它非常简单,二进制还能抗干扰。如果信号是“部分开启”(比如0.4),你可以假设那是0。如果大部分开启(比如0.8),那么可以假设是1。如果你使用有10种可能状态的系统,就很难判断何时发生了错误。这是数字信号如此抗噪声的原因之一。

进制的其他例子

我们一直在使用其他进制,甚至动态变化的进制。我们通常不这么想:

小时、分钟、秒:1:32:04

  • 我们知道这是1小时32分钟4秒。换算成秒就是(1 * 60 * 60) + (32 * 60) + 4。

英尺和英寸:3′ 5″

  • 这是3英尺5英寸,即3 * 12 + 5英寸。

磅和盎司:8磅,5盎司

  • 因为1磅等于16盎司,所以这是8 * 16 + 5盎司。我们一直在使用16进制数制!

结语

数字 10 在任何数制中意味着我们已经进位了一次基。例如, 10 在二进制中表示二, 10 在十进制中表示十,而 10 在十六进制中是十六。

如何区分这些数字,如何判断用了什么基?程序员通常会在 0b 二进制数字前加上。所以二进制中的2是

0b10

类似地,他们会写 0x 在十六进制数字前。所以十六进制中的16是:

0x10

如果没有符号(0b 或者 0x)在前面,我们就假设是十进制,一个普通数字。

现在去享受你的新知识吧!

本系列其他文章

  1. 数制(Number Systems)与进制(Bases)
  2. GUID 快速指南
  3. 理解 Quake 的快速平方根倒数(Fast Inverse Square Root)算法
  4. 计算机网络简明入门
  5. 使用异或(XOR)交换两个变量
  6. 理解大端(Big Endian)与小端(Little Endian)字节序
  7. Unicode 与你
  8. 关于二进制文件格式的一点小曲
  9. 排序算法(Sorting Algorithms)

加入 45 万月度读者

喜欢这篇文章?还有更多内容能帮你建立持久、直观的数学理解。加入通讯以获取额外内容和最新更新。