2 的补码=各位取反再加 1;有了它,减法可以直接用加法电路算,负数也只是一个正常的二进制数。
2.3 讲移位时留下了伏笔:负数怎么表示。这一节给出计算机用的答案。
一、先看那个笨办法
最直觉的做法是【拿最高位当符号位】:0 表示正、1 表示负。 它有两个麻烦:一是出现了 +0 和 -0 两个零;二是加法器没法直接处理它,做加法要额外判断符号。 教材要的表示法必须满足一条:让减法也能用加法做——这样电路里只留加法器就够。
二、2 的补码怎么做
做法只有两步:各位取反,再加 1。 以 8 位求 -3 为例:3 是 0000 0011 → 取反 1111 1100 → 加 1 得 1111 1101。 此时 5 + (-3) 就是 0000 0101 + 1111 1101 = 1 0000 0010。 最高位进上来的 1 被丢掉,剩下 0000 0010,正是 2——减法就这么被做成了加法。
三、这招为什么管用
关键在一句话:一个数和它的补码相加,结果必然是一个【最高位进 1、低位全 0】的数。 而最高位那个 1 会溢出丢掉,于是结果是 0——这正是【加上相反数等于零】的定义。 所以补码不是巧合,是为加法器量身定做的负数表示法。 顺带一个结果:在这套表示法里【不够减】不存在,3 - 5 直接就得 1111 1110,它读作 -2。
四、代价:符号位借走了最高位
有得必有失:最高位被拿去当符号位,能表示的绝对值范围就小了一半。 8 位的范围因此从 0~255 变成 -128 ~ 127。 这个范围直接决定了后面所有【溢出】问题的边界——127 + 1 会变成 -128。 记住这条,以后见到【为什么加着加着变成负数了】,就知道答案在哪儿。
五、动手 Demo:用【2 的补码】把减法变成加法
补码的做法:各位取反,再加 1。 有了它,减法可以直接交给加法电路。
for a, b in [(5, 3), (3, 5), (10, 7)]:
nb = (-b) & 0xFF
print("%d - %d : %s + %s = %s"
% (a, b, format(a, "08b"), format(nb, "08b"), format(a + nb, "09b")))
实跑输出:
--------------------------------------------------------------------
用【2 的补码】把减法变成加法
--------------------------------------------------------------------
**补码的做法:各位取反,再加 1。** 有了它,减法可以直接交给加法电路。
以 8 位为例,求 -3:
3 0000 0011
各位取反 1111 1100
加 1 1111 1101 <- 这就是 -3
于是 5 - 3 变成 5 + (-3):
0000 0101 (5)
+ 1111 1101 (-3)
-----------
1 0000 0010 <- 最高位进上来的 1 丢掉,剩 0000 0010 = 2
算式 被减数 减数的补码 用加法算出来的 正确结果
-------------------------------------------------------------------
5 - 3 00000101 11111101 100000010 2
3 - 5 00000011 11111011 011111110 -2
10 - 7 00001010 11111001 100000011 3
注意 3 - 5 的结果:得 1111 1110,**它就是 -2 的补码**。
所以这套表示法里没有【不够减】这回事——负数也是一个正常的二进制数。
**一点代价:最高位被借去当符号位,8 位能表示的范围变成 -128 ~ 127。**
3 - 5 得到 1111 1110,它就是 -2 的补码——这个表示法里没有【不够减】这回事。 减法电路没了,只剩加法器——这是补码存在的全部理由。
30 秒实验
- 手算 -5 的 8 位补码(取反加 1),再用它算 8 + (-5)。
- 把 127 写成 8 位补码,加 1 看结果变成什么。
常见误区
- 以为补码只是【一种写法】。它是为了让加法电路能算减法而设计的,目的极明确。
- 忘记范围变了一半。8 位从 0~255 变成 -128~127,溢出就在这一步埋下。
自测三问(点开对答案)
1. 2 的补码怎么求? —— 各位取反,再加 1。 2. 补码为什么能让减法变成加法? —— 一个数与其补码相加会产生最高位进位,去掉这位后结果是 0,正是相反数的定义。 3. 8 位补码能表示的范围是多少? —— -128 ~ 127(最高位被用作符号位,范围少了一半)。- ☐ 下一节(2.5 逻辑右移与算术右移的区别)
相关笔记
- 移位运算与乘除运算的关系 — 溢出在这里第一次露头
- 逻辑右移与算术右移的区别 — 负数出现后,右移就得分两种
- 🔵 计算机基础 · 索引 — 硬件底层的入门课
- ⚙️ 程序是怎样跑起来的 · 索引