数字与进制
# 20.数字与进制
理解进制,是理解计算机运算规则的基础
# 数字的产生
文明萌芽之前,人类的祖先还没有「数」的概念。然而人类终究是要与较大的数打交道的,除了每天的吃喝拉撒,祖先们渐渐需要面对「打到了多少猎物」、「部落有多少人口」这类简单的统计问题。他们开始动用身上的各种部位,例如手指与脚趾。后面也曾用石子、结绳计数等方式。
假定某个人有 4 只猫,他可能画 4 只猫作为记录。
当然,每增加一只猫,就画一只猫,可能太麻烦了;我们能不能用一条竖线来表示呢?可以的,例如:| | | | 表示 4 只猫
但若某人有 27 只猫,用画 27 个竖线来表示猫的只数的方法就显得很荒谬了:| | | | | | | | | | | | | | | | | | | | | | | | |
于是,古人们想到得有一种更好的办法才行,进制就这样诞生了。我们日常生活中都是用阿拉伯数字,当一位数表示不了的情况下,我们就会进位,也就是从一位数变成两位数,再进位到三位数,以此类推。这个进位规则可以 理解为“逢十进一”。
一个数字依据位置的不同代表不同的数量。数字的位置和数字的大小一样,都是很重要的。严谨地说,数字的位置更重要,100 和 1000000 中都只有一个 1,但我们知道一百万比一百要大得多。
# 什么是数字系统
到了现代,我们经常用阿拉伯数字来表示数量,例如一辆车,我们用 1 表示,两辆车,我们用 2 表示。但其实,0,1,2,3,4,5,6,7,8,9 这十个数字,都是符号而已。符号不仅仅可以用于表示数量,也可表示事物。例如我们说“3 号选手”, 这里的 3 就是指某个人;又例如 2022 年 3 月 19 号,这里的 3 指的是 3 月。
有没其他符号可以表示 ”数量 2“ 这个概念呢? 有的。比如在罗马数字中,1~20 分别为:Ⅰ、Ⅱ、Ⅲ、Ⅳ、Ⅴ、Ⅵ、Ⅶ、Ⅷ、Ⅸ、Ⅹ、Ⅺ、Ⅻ、XIII、XIV、XV、XVI、XVII、XVIII、XIX、XX。也就是说,对于数量的表示而言,我们用什么符号(阿拉伯数字、罗马数字)都是可以的,只要人们能理解它。比如完全可以约定,J 是 11,Q 是 12, K 是 13(在扑克牌中有用到)。
在国内,对数字的表示也可以用中文,例如壹贰叁肆伍陆柒捌玖,纸币上就有写明。
在法国,不同数字的表示如下:
法语数字单词表(图片来自网络)
# 什么是标准数字系统
如果各国都采用各自的数字系统,那么国家之间沟通数字是很困难的。例如我们可能看不懂法国的单词表,法国人也看不懂我们的壹贰叁肆伍陆柒捌玖,有没这样一个数字系统,全球各地通用的呢?
有,就是阿拉伯数字,在全世界大部分范围内,阿拉伯数字都是被人们所知道的。也就是说,阿拉伯数字是一种标准。
标准的好处在于,全世界的人都知道,不用担心引起误解。比如在罗马数字中,Ⅹ 代表数字 10,但如果不事先说明这是罗马数字,人们也可能认为这是一个英文字母 X。
# 数字的其他含义
数字并不仅仅用于做计算。例如:
- 在长跑比赛时,为了识别运动员,通常会给每一位运动员编一个号码,显然,这些号码仅仅表示不同的运动员而已,没有数量大小的含义。
- 电话号码:仅仅是一串数字,也不用于计算
- 邮政编号:一个区域的编号,也不用于计算
- ...
# 十进制介绍
英文为 Decimal System,缩写 Dec 或 D
十进制中,不同位置的数字,代表的值是不一样的,例如下面的 1 含义各不相同:
- 12 = 1×10 + 2,这里的 1 代表 10
- 120 = 1×100 + 2×10,这里的 1 代表 10 × 10 = 100
- 1200 = 1×1000 + 2×100,这里的 1 代表 10 ×10 ×10 = 1000
每个 1 的乘数是不一样的,乘数也可以理解为权重(权值)。12 里的 1,权值是 10, 120 里的 1,权值是 10, 1200 里的 1,权值是 10。注意,个位数的值可以看成是 个位数 × 10 的 0 次方,因为任何数的 0 次方都等于 1,所以也可以写成:
- 12 = 1×10 的 1 次方 + 2×10 的 0 次方
- 120 = 1×10 的 2 次方 + 2×10 的 1 次方
- 1200 = 1×10 的 3 次方 + 2×10 的 2 次方
越高位的数字,权重越大。每个乘数都比右边大十倍(也就是乘以十):
千位数 | 百位数 | 十位数 | 个位数 |
---|---|---|---|
1000(10 的 3 次方) | 100(10 的 3 次方) | 10(10 的 1 次方) | 1(10 的 0 次方) |
以此类推,一万和一百万,乘数也不同,但都是 10 的次方。
注意,小数部分也是这样,只不过是 10 的 负 N 次方:0.75 = 7×10 的负 1 次方 + 5×10 的负 2 次方
在十进制中,十也称为基数。
# 八进制介绍
八进制的英文:Octal,缩写为 O 或者 o (英文字母的 o)
如果,如果人类像卡通人物那样,每只手上只有 4 个手指会怎样呢?我们可能就不是用十进制,而是八进制。当要表示超过 “八”这个概念的数字,我们需要进位,逢八进一。例如:
要表示的数量 | 数字 |
---|---|
一 | 1 |
二 | 2 |
三 | 3 |
.... | ... |
七 | 7 |
八 | 10 (逢八进一) |
注意,这里的每个数权重不一样了:
- [12] 八进制 = 1×8 + 2 , 这里的 1 代表 8
- [122] 八进制 = 1×64 + 2×8 + 2 =[82] 十进制, 这里的 1 代表 8 × 8 = 64
在 [122] 八进制 中,从左到右每个位的乘数分别是 64,8,1 ,每个乘数都比右边大八倍(也就是乘以八)。
除了十进制,还有十六进制、八进制和二进制等等常用的进制,在人类早期文明历史中,玛雅人采用的还是二十进制。
在八进制里,基数是八,其他进制的基数同理。为了表示是八进制的数字,我们可以用括号将数字包住,并且用下标表明这是几进制,例如八进制的 12: (12)8
# 二进制介绍
二进制的英文:Binary,缩写为 B。
二进制,是计算机内采用的标准数字系统。
而最简单的进制系统中,无疑是二进制,逢二进一,也是计算机中采用的进制。可以说,计算机只认识二进制,只处理二进制,像我们看到的文本、图像、视频、各种软件等等,最终都会转化为二进制让计算机处理。
要表示的数量 | 数字 |
---|---|
一个 | 1 |
二个 | 10 = 1+1 (逢二进一) |
三个 | 11 = 10 +1 |
四个 | 100 = 11+1 |
.... | ... |
七个 | 111 |
八个 | 1000 = 111 + 1 |
例如 [111] 二进制中,每位数的乘数也不一样了,每个乘数是右侧乘数的两倍。二进制的 111 = 十进制中的 1×4 + 1×2 + 1×1, 也就是说二进制的 111 等于十进制的 7。
111 = 1×4 + 1×2 + 1×1 = 1×2 的 2 次方 + 1×2 的 1 次方 + 1×2 的 0 次方
当二进制的位数很多时,一连串看起来有点头皮发麻,所以一般每隔 4 为就用一个空格分割。
同理,十进制也有这样的习惯,例如每隔 3 位用逗号分割,这叫三位分节法,例如 1,000 1,000,000
拿二进制数 1011 0110 举例,我们可以用相同的方法转成十进制。
1011 0111 (二进制) =
1x128 + 0x64 + 1x32 + 1x16
+0x 8 + 1x4 + 1x2 + 0x1
= 182 (十进制)
2
3
4
示意图:
二进制的加减乘除例子:
从上面的例子中可以看到二进制算术运算的两个特点:
- 二进制数的乘法运算可以通过若干次的“被乘数(或零)左移 1 位”和“被乘数(或零)与部分积相加”两种操作完成
- 二进制数的除法运算能通过若干次的“除数右移 1 位”和“从被除数或余数中减去除数”两种操作完成
如果我们再能设法将减法操作转化为某种形式的加法操作,那么加、减、乘、除运算就全部可以用“移位”和“相加”两种操作实现了。利用上述特点能使运算电路的结构大为简化。这也是数字电路中普遍采用二进制算术运算的重要原因之一。
# 十六进制
十六进制的英文:Hexadecimal,缩写为 X。
如果说要用超过十进制的进制,例如十六进制,需要一个符号来代表 “十” 这个概念,人们用字母来表示 “十 到 十五” 之间的数:
要表示的数量 | 数字 |
---|---|
一个 | 1 |
二个 | 2 |
三个 | 3 |
四个 | 4 |
.... | 5 |
六个 | 6 |
七个 | 7 |
八个 | 8 |
九个 | 9 |
十个 | A |
十一个 | B |
十二个 | C |
十三个 | D |
十四个 | E |
十五个 | F |
十六个 | G |
也就是说 [A] 十六进制 = [10] 十进制
[10]十六进制 = 1×16 + 0 = [16] 十进制,进位规则是逢十六进一。
# 不同进制的对照表
十进制 | 二进制 | 八进制 | 十六进制 |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 10 | 2 | 2 |
3 | 11 | 3 | 3 |
4 | 100 | 4 | 4 |
5 | 101 | 5 | 5 |
6 | 110 | 6 | 6 |
7 | 111 | 7 | 7 |
8 | 1000 | 10 | 8 |
9 | 1001 | 11 | 9 |
10 | 1010 | 12 | A |
11 | 1011 | 13 | B |
12 | 1100 | 14 | C |
13 | 1101 | 15 | D |
14 | 1110 | 16 | E |
15 | 1111 | 17 | F |
# 各个进制的表示前缀
我们之前为了表示是八进制的数字,我们可以用括号将数字包住,并且用下标表明这是几进制,例如八进制的 12: (12)8
也可以有这样的表示方法:给数字加个前缀,取自各进制的英文首字母(大小写均可):
进制基数(radix) | 前缀 | 示例 |
---|---|---|
二进制 binary | 0b 0B | 0b11 = 2+1=3 |
八进制 octal | 0o 0O | 0o11 = 8+1=9 |
十进制 decimal | 无前缀 | 11 = 11 |
十六进制 hex | 0x 0X | 0x11 |
# 进制转换
由于多种进制的存在,它们之间的相互转换是经常会遇到的事情,也请读者务必掌握。
# 二进制与十进制
二进制转十进制:只需将每位数乘以对应的乘数,再将结果相加即可。
例如 (1011.01)2 = 1×23 + 0×22 + 1×21 + 0×2-1 + 1×2-2 = (11.25)10
十进制转二进制:采用“除 2 取余,逆序排列”法:
- 首先用 2 整除一个十进制整数,得到一个商和余数
- 然后再用 2 去除得到的商,又会得到一个商和余数
- 重复操作,一直到商为小于 1 时为止
- 然后将得到的所有余数全部排列起来,再将它反过来(逆序排列)
举例:将 (173) 转为二进制过程如下
原理是什么呢? 我们自己可以推理下。
前面我们知道二进制数转十进制数是怎么转换的:
- (1011.01)2 = 1×23 + 0×22 + 1×21 + 1×20 + 0×2-1 + 1×2-2 = 11.25
- 一个通用公式:假设十进制整数为 (S) 10,其转换为二进制数,值为 (kn kn-1 ………… k0)2 ,我们可以列出这样的公式: (kn × 2n + kn-1 ×2n-1 + ………… + k0 × 20 ) = (S) 10
- 我们将等号两边同时除以 2,可以得到商为 (kn × 2n-1 + kn-1 ×2n-2 + ………… + k1)2,余数为 k0 因为 1 个二进制数最多表示 1,除以 2 肯定是除不尽的,会有余数),也就是说,我们计算出了第一个二进制数是 k0;
- 再次将等号两边同时除以 2,可以得到余数 k1
- …………以此类推,就可以求得二进制数的每一位了!
小数转二进制:采用"乘 2 取整,顺序排列"法:
- 用 2 乘十进制小数,可以得到积,将积的整数部分取出
- 再用 2 乘余下的小数部分,又得到一个积,再将积的整数部分取出
- 重复操作,直到积中的小数部分为零,此时 0 或 1 为二进制的最后一位,或者达到所要求的精度为止
例如,将 0.125 转换为二进制,步骤如下:
0.125 * 2 = 0.25 ------0
0.25 * 2 = 0.5 ------0
0.5 * 2 = 1.0 ------1
当小数部分为 0 就可以停止乘 2 了,然后正序排序就构成了二进制的小数部分:0.001
其原理,和十进制转二进制的原理一样。
特别注意,不是所有的十进制数都能转化为有限位二进制数的。举个例子,十进制小数 0.7,转化为二进制小数是:0.1011001100110......,是无限循环小数,循环节是 0110。
那些能分解为以 (1/2)^n 为单位的十进制小数,才可以转化为有限位数的二进制小数。
如十进制数:13/16=0.8125,它可以是拆成:13/16=1/2+1/4+1/16,或者直接可以看作是 13 个 1/16 所组成。而 1/2,1/4,1/16 这些数都是符合 (1/2)^n 形式的数。
如果又有整数又有小数:将整数部分和小数部分先单独转为二进制,再合在一起就可以了。
# 二进制与十六进制
由于 4 位二进制数恰好有 16 个状态,而把这 4 位二进制数看作一个整体时,它的进位输出又正好是逢十六进一,所以只要从低位到高位,将整数部分每 4 位二进制数分为一组,并代之以等值的十六进制数。
同时从高位到低位将小数部分的每 4 位数分为一组并代之以等值的十六进制数,即可得到对应的十六进制数。
例如,将二进制数(01011110.10110010)化为十六进制数时可得:
若二进制数整数部分最高一组不足 4 位时,用 0 补足 4 位:小数部分最低一组不足 4 位时,也需用 0 补足 4 位。
十六进制转二进制,只需反过来,每一位十六进制数用等值的 4 位二进制数代替就行。例如,将 (8FA.C6) 转为二进制:
# 二进制与八进制
与十六进制和二进制,互相转换基本一样。
在将二进制数转换为八进制数时,只要将二进制数的整数部分,从低位到高位每 3 位分为一组,并代之以等值的八进制数,同时将小数部分从高位到低位每 3 位分为一组并代之以等值的八进制数就可以了。
二进制数最高一组不足 3 位或小数部分最低一组不足 3 位时,仍需以 0 补足 3 位。
例如,若将二进制数 (011110.010111) 化为八进制数,则得到:
反之,若将八进制数转换为二进制数,则只要将八进制数的每一位代之以等值的 3 位二进制数即可。例如,将 (52.43) 转换为二进制数时,得到:
# 其他进制
8 进制转 10 进制、16 进制转 10 进制:就只能按权重展开后,相加得到十进制了。
10 进制转 8 进制、10 进制转 16 进制,可以先转为 2 进制,再分组转换。
# 进制转换工具
如果我们已经知道如何转换进制了,当数字很大的时候,自己转换就是浪费时间,并且容易出错,因此,我们可以借住计算机帮助我们转换。
win10 自带的计算机,按下快捷键 win + R,输入 calc 然后回车。然后菜单栏里选择程序员:
# 练习
- 手动实现各进制下的转换
- 自行实现在各进制下的加减乘除
- 用计数器验证结果