补码
复习
- 半加器:半加器用异或门算和、与门算进位,完成两个一位二进制数相加
- 全加器:全加器能处理低位进位,可串联构成多位加法器
- 有符号数与原码:原码用符号位表示正负;反码需要回卷进位参与运算,且仍有两个零
TL;DR
- 负数的补码 = 反码 + 1
- 补码可以直接参与加法,正数的补码就是它本身
- 快速求法:从右往左找到第一个 1,该位及右边不变,左边全部取反
- 补码只有唯一的零,是现代计算机表示有符号数的标准
正文
还差的那 1
前面讲有符号数时我们发现,反码可以用回卷进位算 5 + (-2),但产生溢出进位时还要把它加回低位。
那么,我们为什么不能在负数的编码里事先加 1 呢?
于是,新的编码诞生了。
这种新编码干脆在反码里 事先加 1,让加法器把溢出的进位直接丢掉。
先用最简单的目标验证:我们希望 2 + (-2) = 0。
2 的反码是 0010,让 -2 的反码 1101 再加 1,那么这种新编码中的 -2 应当表示为 1110。
现在算 0010 + 1110:
0010
+ 1110
-------
(1)0000 ← 第 5 位进位溢出,直接丢弃,得到 0000
正好是 0。成了。
思考题 1
看起来一切都很顺利,但是有一个疑虑我们之前还没消灭:补码解决了正负零的问题了吗?
补码的定义
负数的补码 = 反码 + 1。
而正数不需要求补,补码就是它自身。
2₁₀的补码 =0010-2₁₀的补码 =1101+0001=1110
这个编码直接从算式 2 + (-2) = 0 中推导出来,可以直接参与计算,叫作 补码 。
思考题 2
请分别写出
-5的 8 位原码、反码和补码。
快速求补码
每次都“取反再加一”有点麻烦。观察一下可以发现一个更快的办法:
要求一个负数的补码,只需要先写出这个负数的相反数(也是它的绝对值),在这个正数的原码表示中,从右往左找到第一个 1,这个 1 以及它右边的位保持不变,左边的位全部取反。
例子:求 -6 的补码。
- 先写出
6的二进制0110。 - 最右边的 1 在右起第二位,该位及其右边的 0 保持不变,
01|10,左边(含符号位)取反,得到1010。 1010就是-6的补码。
原因也不难想:
- 正数原码的某一位上如果是
0,那么反码会将它取反变成1,再加1时,就会进位,又变成0——原来是0,现在还是0- 所以,从右往左第一个
1右边的0会一路进位
- 所以,从右往左第一个
- 而正数原码的某一位上如果是
1,那么反码会将它取反变成0,再加1时,不会 进位,仍然为1——原来是1,现在还是1- 而第一个
1的位置恰好是:一连串0变成反码加1时进位停止的地方 - 自这一位起,左边的位因为没有进位了(相当于没有了
+ 1),只有取反 + 1中的取反,所以编码会全部反转
- 而第一个
- 所以右边的低位不变、左边全部翻转。
思考题 3
为什么
1000₂(补码)是-8₁₀?
一个特殊的数
4 位补码一共有 2⁴ = 16 种位模式,所有加法都按模 16 回绕。0000 到 0111 表示 0 到 +7,1000 到 1111 表示 -8 到 -1。1000 是一个特殊值:它表示 -8,而 -8 的相反数 +8 超出了 4 位补码的范围,所以对它再求补,仍会得到 1000。
也就是说,以 0111 和 1000 之间为边界,正数和负数分别排在模 16 的两侧。用图表示,大概是这种感觉:
所以 4 位补码能表示的范围是 1000(-8)到 0111(+7)。8 位就是 1000 0000(-128)到 0111 1111(+127)。 负数的范围比正数多一个。
思考题 4
在 8 位补码里,-128 的原码是什么?-128 + 1 的结果是多少?为什么?
补码的优点
补码有几个重要的优点:
-
统一了加减法运算
- 减法可以转换为加上一个负数
- 负数可以用补码表示
- 所以只需要加法器就可以完成减法
-
避免了正负零的问题
- 原码中 +0 是 0000 0000,-0 是 1000 0000
- 补码中只有一个零:0000 0000
-
简化了硬件设计
- 不需要专门的减法电路
- 可以复用加法器
小结
知识点
- 补码 = 反码加一
- 快速求补码的方法
- 补码的表示范围
- 补码的三个优点
参考资料
- Wikipedia(zh):补码:补码的详细介绍
- Bilibili:计算机怎样计算减法
思考题答案(仅供参考)
思考题 1
补码没有正负零。
负零的原码 1000 经 取反加 1 计算后,恰好变成了绝对值更大的新负数 -8(1000,补码表示法),从而消除了重复的零并多出一个表达名额。
回想一下补码的计算规则,我们试着算一下:
- 负零原码:
1000 - 数值位取反:
1111 - 末位加
1:1111 + 1 = 10000
因为只有 4 位,溢出的最高位会被自动舍弃,结果变成了 0000。
最终,正零 0000 和变换后的负零都指向了 0000,零因此只有一个。
原本被“负零”占用的 1000 并没有浪费,它被顺理成章地用来多表示一个绝对值更大的最小负数(如 4 位二进制中的 -8)。
思考题 2
5的 8 位原码:0000 0101-5的 8 位原码:1000 0101-5的 8 位反码:1111 1010(符号位不变,数值位取反)-5的 8 位补码:1111 1011(反码加一)
思考题 3
4 位补码共有 2⁴ = 16 种位模式,所以加法按模 16 进行。把位模式当作无符号数看,1000 是 8;把它当作 4 位补码看,最高位为 1,因此它表示 8 - 16 = -8。
这样解释后,正数是 0 到 +7,负数是 -8 到 -1,刚好用完 16 种位模式。1000 + 1000 的结果是 (1)0000,丢弃溢出进位后得到 0,因此 -8 在这个固定宽度里会求补回自己;它的真正相反数 +8 则无法用 4 位补码表示。
思考题 4
- -128 没有原码表示(因为正值 128 超出了 8 位能表示的正数范围)
- -128 + 1 =
1000 0000 + 0000 0001 = 1000 0001(-127)- 这就是为什么 8 位补码的范围是 [-128, 127]
- -128 是一个特殊值,它没有对应的原码表示。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪