03 算术运算与存储器

二进制与二进制速算

按无符号整数整理二进制怎么读、怎么算:位权与位宽,二进制和十进制互换,加减法的进位与借位,乘除法,按位运算与奇偶,以及几个速算方法和固定位宽的限制。文末有一个交互练习:点选 128 到 1 的位权,凑出目标数。

前一篇先看了 CPU 里哪些部件负责计算、保存和控制。这一篇把二进制的读法和计算单独整理出来,后面搭运算电路时,就能先判断结果应该是什么。

下面先按无符号整数,也就是 0 和正整数来计算。负数和补码,留到需要处理它们时再展开。

1. 二进制怎样表示一个数?

十进制用 0 到 9,某一位满十就向左进一。二进制只用 0 和 1,某一位满二就向左进一,所以数到 1 以后,下一个数就是 10。

十进制 0 1 2 3 4 5 6 7 8
二进制 0 1 10 11 100 101 110 111 1000

一位就是一个只能放 0 或 1 的位置。 八位组成一个字节;一个数用多少位来放,就叫它的位宽。101 和 0101 都表示 5,前面的 0 可以用来补齐约定的位宽。

十进制从右往左是个位、十位、百位,二进制从右往左则是 1、2、4、8、16、32……。每往左一位,代表的大小翻一倍。这个位置代表的大小,叫位权。

位权:        8  4  2  1
二进制 0101: 0  1  0  1

0101 = 0×8 + 1×4 + 0×2 + 1×1 = 5

每一位都能选 0 或 1,所以四位一共有 2×2×2×2 = 16 种组合,表示 0 到 15。一般来说,n 位有 2^n 种组合,无符号数的范围是 0~2^n−1。

2^n 表示 n 个 2 相乘,例如 2^4 = 2×2×2×2 = 16。范围的上限还要减 1,是因为这些组合从 0 开始编号,0 也占一种。后文用 10₂ 这样的下标表示二进制;没有下标时,按表头或上下文说明的进制读。

电路里,多位数据可以用多根线一起传,每根线负责一位。这组线合起来就是数据总线。

2. 二进制和十进制怎样互换?

二进制转十进制:把为 1 的位置加起来

例如 110101:

位权:   32  16   8   4   2   1
各位:    1   1   0   1   0   1

110101 = 32 + 16 + 4 + 1 = 53

为 0 的位置不贡献数值,可以直接跳过。

十进制转二进制:从大到小拆成位权

反过来把 53 写成二进制,就从不超过 53 的最大位权 32 开始。能减就写 1,不能减就写 0:

当前位权 剩下的数够不够减 这一位写什么 减完剩多少
32 53 够减 32 1 21
16 21 够减 16 1 5
8 5 不够减 8 0 5
4 5 够减 4 1 1
2 1 不够减 2 0 1
1 1 够减 1 1 0

从上往下读这一列,就得到 110101。中间不够减的位置也要写 0,否则后面各位的位置就变了。

十六进制可以把二进制写短一点:每四位对应一位十六进制。十六进制的 A~F 分别表示 10~15,例如 1111 写成 F;0011 1100 分成两组,分别是 3 和 12,因此写成 3C,表示十进制的 60。

3. 加法和减法:进位与借位

加法满二进一

最基本的规则是:

本位相加 二进制结果 本位留下什么 向高一位进多少
0 + 0 0 0 0
0 + 1,或 1 + 0 1 1 0
1 + 1 10 0 1
1 + 1 + 低位进来的 1 11 1 1

例如 0101 + 0011,也就是 5 加 3:

   0101
 + 0011
 ------
   1000

从右往左算:

  1. 最右边:1 + 1 = 10,写 0,向左进 1。
  2. 第二位:0 + 1 + 进位1 = 10,写 0,再向左进 1。
  3. 第三位:1 + 0 + 进位1 = 10,写 0,再向左进 1。
  4. 最左边:0 + 0 + 进位1 = 1,写 1。

得到 1000,就是 8。高一位要把低位送来的进位一起算进去。

减法不够减,就向高位借

二进制中,向左边借来的 1,在当前位相当于 2,也就是二进制的 10。所以本来不够算的 0 − 1,借位后就变成 10 − 1 = 1。

例如 0110 − 0011,也就是 6 减 3:

   0110
 - 0011
 ------
   0011

最右边的 0 − 1 不够,向左借 1,本位得到 1;左边原来的 1 已经借走,变成了 0。第二位现在又是 0 − 1,再向更高位借 1,也得到 1。剩下的高两位都是 0 − 0,所以结果为 0011,就是 3。

如果左边也是 0,就需要继续向更高位借。例如 1000 − 0001,也就是 8 减 1。可以先把借位后的各列数量写出来,再逐列相减:

借位过程 权重 8 的列 权重 4 的列 权重 2 的列 权重 1 的列
原来的 1000 1 0 0 0
从权重 8 借一个到权重 4 0 2 0 0
从权重 4 再借一个到权重 2 0 1 2 0
从权重 2 再借一个到权重 1 0 1 1 2
各列减去 0001 0 1 1 1

表中的 2 是借位过程中暂时放在该列的数量,不是二进制数里允许写出的数字。每一步都只是换一种方式分配同一个 8,例如最后借完时 0×8 + 1×4 + 1×2 + 2×1 = 8。减去最低位的 1 后,得到 0111,也就是 7。

这里先讨论被减数不小于减数的情况。得到负数时该怎样表示,后面和补码一起讲。

4. 乘法和除法

乘法:每一位只有“加上”或“不加”

乘数的某一位是 0,这一行就是 0;是 1,就把被乘数写下来,再按这一位的位置向左错开。

例如 101 × 11,也就是 5 乘 3:

    101
 ×   11
 ------
    101   乘数最低位为 1:加上 101
 + 1010   高一位也为 1:把 101 左移一位再加
 ------
   1111

这里相当于算 5×1 + 5×2 = 15,结果 1111 就是 15。

除法:够减一次就商 1,不够就商 0

和十进制竖式除法一样,从左往右读被除数,每次把下一位接到当前余数的右边,再判断够不够减去除数。

“接到右边”可以按一个具体动作来算:旧余数左移一位,再添入刚读到的位,也就是 当前值 = 旧余数 × 2 + 新读入的位。例如旧余数是 1,再读到一个 1,当前值就是二进制 11,数值为 3。

例如 1101 ÷ 11,也就是 13 除以 3。下表里的数都用二进制表示:

读到哪一位 当前拿来除的数 这一位的商 本步余数
第 1 位:1 1,不够减 11 0 1
第 2 位:1 11,够减 11 1 0
第 3 位:0 0,不够减 11 0 0
第 4 位:1 1,不够减 11 0 1

商按顺序是 0100,去掉前面的 0 就是 100,余数为 1。也就是商 4、余 1;用 4×3 + 1 = 13 可以核对。除数不能为 0。

为什么每一步只需要判断“能不能减一次”?上一步的余数一定小于除数;乘 2 再加上新读入的 0 或 1 后,当前值仍小于除数的两倍。所以本步的商只能是 0 或 1,减一次已经足够,不会需要写出商 2。

5. 按位运算、奇偶与计数

按位运算是把对应位置分别交给逻辑门。例如两组四位数据做 AND,得到的仍是四位:

  0101
  0011
  ----
  0001   每一列分别做 AND

对同一对输入,几种运算的结果可以放在一起看:

对 0101 和 0011 做什么 结果
加法 1000
按位 AND 0001
按位 OR 0111
按位 XOR 0110

加法会在位与位之间传递进位;按位 AND、OR、XOR 则是各个位置分别判断。因此 OR 与加法得到的结果可能不同。

把一组位用 XOR 合并,每一步的结果再与下一位做 XOR,最后就只留下一个 0 或 1。从 0 开始算,每遇到一个 1,结果就翻转一次;遇到 0 则不变。所以最后为 1,说明有奇数个 1;最后为 0,说明有偶数个 1。

用同一个 0101 来区分三件事:

要判断什么 答案 理由
这个数是不是奇数 是 它表示 5;最低位是 1,其余位置代表的都是偶数
里面有奇数个 1 吗 没有 一共有两个 1,所以各位 XOR 的结果是 0
里面一共有几个 1 两个 要输出数量 2,也就是二进制的 10

先弄清要求的是数值、奇偶判断,还是数量,才能知道应该输出几位、算什么。

6. 几个速算方法,以及位宽的限制

看到的形式 可以怎样读 例子
只有一个 1,后面全是 0 直接看这个 1 所在位置的位权 10000 是 16
n 位全是 1 比下一个位权小 1,即 2^n−1 1111 = 16−1 = 15
最低位是 0 或 1 分别是偶数或奇数 1010 是偶数,1011 是奇数
左移一位,右边补 0 位数足够时相当于乘 2 0011 → 0110,即 3 → 6
右移一位,左边补 0 对无符号整数,相当于除以 2 后取整数部分 0111 → 0011,即 7 → 3,原最低位说明余数为 1

纸上计算时可以往左多写一位,固定宽度的电路则未必能保存多出来的那一位。例如:

1111 + 0001 = 1 0000

15 加 1 得到 16,需要五位才能表示。若输出只保留四位,就只剩下 0000;额外的进位要用另一条信号表示,才能保留这次计算的完整结果。四位的 1000 左移一位时也一样:完整结果是 10000,若只留四位就变成 0000。

因此,速算时除了看运算,还要看允许保留多少位。这一篇先把数算清楚;后面搭电路时,再把结果位、进位以及保存的位置一一对应起来。

7. 二进制交互速算

下面是一个二进制速算练习:点选 128 到 1 的位权,让它们加起来等于目标数。每点一下都会算出当前的和,并提示还差多少或多了多少;凑对以后,会显示目标数的二进制写法。「换一题」随机给出 1~255 之间的新目标,「清空」取消全部选择。

练习是网页上的交互组件,在网站版里可以直接操作;在 GitHub 上阅读时,这里只有这段说明。

目标53

0 = 0还差 53