⚙️ 第二章 用二进制来理解数据
📄 一句话概括

左移一位就是乘 2,右移一位就是除 2——因为每一位的位权都翻了一倍。

📝 本节和前面几节的关系

2.2 讲清了位权,这一节看位权带来的第一个便宜:移位能代替乘除。

一、挪一位,位权全体翻倍

把 5 写成二进制:0101。 左移一位 → 1010,也就是 8 + 2 = 10,正好是 5 的两倍。 右移一位 → 0010,也就是 2,正好是 5 除以 2 取整。 原因就在 2.2 的位权上:整体挪一格,每一位从 2 的 k 次方变成了 2 的 k+1 次方。

二、硬件的账:移位几乎是免费的

真正做乘法要搭乘法器电路——面积大、级数多、慢。 而移位只是【把线接到隔壁的位上】,几乎不花时间、不占面积。 所以在硬件设计和程序优化里,乘 2 的幂常被写成移位:×8 写成左移 3 位。 这也解释了为什么初学汇编的人常看到 shl/shr 这种指令——它们不只是位运算,也是在算乘法。

三、移位不是万能的

移出去的位会丢:8 位的 1000 0000 左移一位,1 就出了边界,结果变成 0。 这就是溢出的最朴素形态——不是算错了,是位不够放。 另外右移对负数是另一回事:要不要保留符号,正是 2.5 要讲的算术右移与逻辑右移之别。

四、动手 Demo:左移一位就是乘 2

二进制的移位,正好对应十进制的乘 2 与除 2。 因为每往左挪一位,都等于把这个数每一位的位权翻了一倍。

for v in [3, 5, 6, 12, 25]:
    print("%2d  %s  左移 -> %s (%d)   右移 -> %s (%d)"
          % (v, format(v, "08b"), format(v << 1, "08b"), v * 2,
             format(v >> 1, "08b"), v // 2))

实跑输出:

--------------------------------------------------------------------
左移一位就是乘 2
--------------------------------------------------------------------

**二进制的移位,正好对应十进制的乘 2 与除 2。**
因为每往左挪一位,都等于把这个数每一位的位权翻了一倍。

  原值     二进制       左移 1 位     等于     右移 1 位     等于
  ----------------------------------------------------------------
  3        00000011     00000110      6        00000001      1
  5        00000101     00001010      10       00000010      2
  6        00000110     00001100      12       00000011      3
  12       00001100     00011000      24       00000110      6
  25       00011001     00110010      50       00001100      12

  拿 5 看一遍就清楚:
    5      = 0000 0101
    左移一位 = 0000 1010 = 8 + 2 = 10  = 5 × 2
    右移一位 = 0000 0010 = 2         = 5 ÷ 2(取整)

  **乘除 2 在电路上不用真做乘法——把线挪一位就行,几乎不花时间。**
  这也解释了为什么代码里【乘 2 的幂】常被写成移位。

5 写成 0101:左移成 1010(10),右移成 0010(2)——乘 2 与除 2 各就各位。 乘除 2 在电路上不用真做乘法——挪一位就行。

30 秒实验

  1. 用纸笔算:13 的 8 位二进制左移两位是多少?结果等于 13 乘几?
  2. 找一个 8 位二进制的数,一直左移,看它什么时候变成 0——那就是溢出。

常见误区

  1. 以为移位是【只有底层才用的技巧】。它是乘除 2 的幂的标准做法,高级语言编译器也在用。
  2. 以为右移总是等于除 2。对负数不一定——要看你用的是哪种右移。
自测三问(点开对答案) 1. 左移一位相当于什么运算? —— 乘以 2;右移一位则相当于除以 2 取整。 2. 为什么移位能代替乘除? —— 二进制每一位的位权都是 2 的幂,整体挪一格等于所有位权翻倍。 3. 移位会不会出错? —— 会——移出去的位会丢,这就是溢出;对负数还要区分算术右移与逻辑右移。

相关笔记