我们来系统性地对比计算机中表示有符号整数的三种主要方式:原码(Sign-Magnitude)、反码(Ones' Complement) 和 补码(Two's Complement)。重点理解它们的差异以及为什么补码最终成为标准。
- 原码(Sign-Magnitude):最高位表示符号,其余位表示数值大小(已被淘汰)。
- 反码(Ones' Complement):负数的符号位不变,其他位取反(仍存在±0问题,效率低)。
- 补码(Two's Complement):当前通用标准,是现代计算机的基石。
核心概念对比
| 特征 | 原码 (Sign-Magnitude) | 反码 (Ones' Complement) | 补码 (Two's Complement) |
|---|---|---|---|
| 表示规则 | 最高位 = 符号位 (0: 正, 1: 负) 剩余位 = 绝对值二进制 | 最高位 = 符号位 正数:同原码 负数:符号位不变,数值位按位取反 | 最高位 = 符号位 正数(含0):同原码 负数:其绝对值的原码 取反 + 1 |
| 0 的表示 | 两种表示: +0:0000 0000 -0:1000 0000 ❌ (浪费一个状态) | 两种表示: +0:0000 0000 -0:1111 1111 ❌ (浪费一个状态) | 唯一表示: 0:0000 0000 ✅ (状态利用率高) |
| 取值范围 (8位) | -127 到 +127 (-2^7+1 到 +2^7-1) | -127 到 +127 (-2^7+1 到 +2^7-1) | -128 到 +127 (-2^7 到 +2^7-1) ✅ (范围更大) |
| 最小负数 (8位) | -127 (1000 0000 → 特殊 -0, 通常 1111 1111 = -127) | -127 (1000 0000) | -128 (1000 0000) ✅ |
| 运算逻辑 | 复杂!需判断符号位 同号相加:数值相加,符号不变 异号相加:数值相减,结果取绝对值大者符号 | 相对简单但仍需处理循环进位 加法直接进行, 但若最高位产生进位,需将进位 加回到最低位 (称为 "end-around carry") | 最简单高效! 加法/减法统一 A + B:直接加 A - B = A + (-B) (-B为B的补码) 忽略最高位进位 ✅ |
| 硬件实现难度 | 最高 (需要复杂的符号位逻辑和大小比较器) | 中等 (需要处理循环进位) | 最低 (只需标准二进制加法器电路) ✅ |
| 优势 | 直观,人类易于理解正负 | 比原码略高效,负数表示逻辑稍自然 | 硬件简单、运算统一、无歧义零、范围更广 ✅ |
| 主要缺陷 | 运算复杂,零歧义,范围小 | 零歧义,运算仍需额外处理,范围小 | 负数表示不直观 (人类需转换计算) |
| 实际应用 | 基本淘汰 (IEEE 754浮点数标准中 符号位 部分是其仅存遗迹) | 基本淘汰 | 现代计算机整数运算的唯一标准 ✅ |
关键差异详解与示例 (8位系统)
-
表示规则 & 0 的表示:
-
原码:
+5:0(符号)000 0101(绝对值) →0000 0101-5:1(符号)000 0101(绝对值) →1000 0101+0:0000 0000,-0:1000 0000❌
-
反码:
+5:同原码 →0000 0101-5:符号位1(1000 0000),数值位绝对值000 0101取反 →111 1010→1111 1010+0:0000 0000,-0:数值位全取反 →1111 1111❌
-
补码:
-
+5:同原码 →0000 0101 -
-5:计算-5+5原码:0000 0101- 取反:
1111 1010 - 加 1:
1111 1011✅
-
0:唯一0000 0000✅
-
-
-
范围 & 最小负数:
- 原码/反码: 最大正数
0111 1111=127, 最小负数1111 1111=-127(原码) /1000 0000=-127(反码)。无法表示-128。 - 补码: 最大正数
0111 1111=127, **最小负数1000 0000=-128** ✅。这个值是特殊约定的(不能通过常规“取反+1”得到+128,因为+128超出正数范围)。
- 原码/反码: 最大正数
-
**运算对比:
5 + (-3)**-
原码 (复杂):
5:0000 0101,-3:1000 0011(符号相反!)- 比较绝对值 |5| > |3| → 结果为正(符号
0) - 数值部分相减:
000 0101-000 0011=000 0010 - 最终结果:
0000 0010(+2) ✅ - 过程繁琐,需要符号判断和比较器。
-
反码:
5:0000 0101-3:符号位不变,数值位取反 →1111 1100- 直接相加:
0000 0101+1111 1100=1 0000 0001(产生进位) - 循环进位:将最高位进位
1加回最低位 →0000 0001+1=0000 0010(+2) ✅ - 需要额外处理进位。
-
补码:
5:0000 0101-3=1111 1101(计算:3=0000 0011→ 取反1111 1100→ 加11111 1101)- 直接相加:
0000 0101+1111 1101=1 0000 0010 - 忽略最高位进位 →
0000 0010(+2) ✅ - 过程最简洁,使用最普通的加法器电路。
-
为什么补码最终胜出?
- 运算统一性: 加法和减法可以用完全相同的硬件电路(一个加法器)实现。这极大简化了CPU算术逻辑单元(ALU)的设计。
- 无歧义的零: 只有一个零表示,避免了冗余状态和比较时的歧义。
- 更广的数值范围: 在相同位数下,补码能表示绝对值更大的最小负数 (
-128vs-127)。 - 高效的硬件实现: 只需一个标准二进制加法器,且进位处理最简单(直接忽略最高位进位)。
- 连续表示: 所有的数(从最小负数到最大正数)在补码空间中形成一个完美的连续闭环,非常适合二进制的存储和计算。
总结记忆表格 (8位模式为例)
| 十进制 | 原码 | 反码 | 补码 (现代标准) |
|---|---|---|---|
| +127 | 0111 1111 | 0111 1111 | 0111 1111 |
| +1 | 0000 0001 | 0000 0001 | 0000 0001 |
| +0 | 0000 0000 | 0000 0000 | 0000 0000 |
| -0 | 1000 0000 ❌ | 1111 1111 ❌ | 不存在 ✅ |
| -1 | 1000 0001 ❌ | 1111 1110 | 1111 1111 |
| -127 | 1111 1111 ❌ | 1000 0000 | 1000 0001 |
| -128 | 无法表示 | 无法表示 | 1000 0000 ✅ |
关键结论: 理解 取反 + 1 的操作是为了构建一个高效的编码系统,使得负数的表示能与加法器无缝协作,从而简化硬件、提高速度并消除歧义。补码是工程效率战胜直观表示的经典案例。