原码、反码、补码详解

计算机底层使用二进制存储数据,但并非直接存储数值对应的二进制形式,而是存储其补码。理解原码、反码、补码是掌握计算机整数运算的基础。

机器数与真值

机器数

一个数在计算机中的二进制表示形式称为机器数。机器数通常用最高位表示符号:

  • 0 表示正数

  • 1 表示负数

  • 其余位表示数值部分。

真值

因为机器数带有符号位,其直接转换的十进制值(将符号位也当作数值)与真正的数值不同。例如,8位机器数 1000 0001 作为无符号数是 129,但作为有符号数(符号位为1)表示的真值是 -1。因此,我们将带符号的机器数所真正代表的数值称为真值。

原码(Sign-Magnitude)

原码是最直观的表示法:

  • 符号位:最高位,0 正,1 负。

  • 数值位:其余位表示数值的绝对值。

示例(8位):

+1 原码:0000 0001
-1 原码:1000 0001
+0 原码:0000 0000
-0 原码:1000 0000

特点:

  • 正数、负数的表示对称。

  • 存在两个零(+0 和 -0),浪费编码空间。

  • 加减法运算复杂,需要根据符号位分别处理,无法直接使用加法器完成减法。

反码(Ones’ Complement)

反码用于简化减法运算,定义如下:

  • 正数的反码 = 原码。

  • 负数的反码 = 符号位不变,数值位按位取反(0→1,1→0)。

示例(8位):

+1 原码:0000 0001 → 反码:0000 0001
-1 原码:1000 0001 → 反码:1111 1110

特点:

  • 反码也是对称的,仍有两个零(0000 0000 和 1111 1111 都表示0)。

  • 减法可以通过加法实现,但需要处理循环进位(端回进位),电路仍较复杂。

补码(Two’s Complement)

补码是现代计算机实际使用的编码方式,它解决了原码和反码的缺陷。

定义:

  • 正数的补码 = 原码 = 反码。

  • 负数的补码 = 反码 + 1。

  • 0 的补码只有一种:0000 0000。

示例(8位):

+1 原码:0000 0001 → 反码:0000 0001 → 补码:0000 0001
-1 原码:1000 0001 → 反码:1111 1110 → 补码:1111 1111

注意:-128 的补码是 1000 0000,没有对应的原码和反码。

特点:

  • 只有一个零,表示范围多一个负数。

  • 减法可以直接用加法器完成:A - B = A + (-B),其中 -B 用补码表示。

  • 硬件实现简单,溢出判断也容易。

为什么计算机使用补码?

  • 统一加减运算
    补码下,减法可以统一为加法,无需设计专门的减法器。

  • 消除 ±0 歧义
    补码只有一种零,所有编码都能用于有效数值。

  • 表示范围更均衡
    8位补码范围 -128 ~ +127,比原码/反码多一个负数。

  • 溢出检测简单
    只需观察最高位与次高位的进位情况。

因此,几乎所有现代计算机的整数运算都采用补码表示。

补码的求法与还原

从负数求补码

对于负数 -x(x > 0):

  • 写出 x 的二进制表示(不足 n 位前面补 0)

  • 取反(0 变 1,1 变 0)

  • 加 1

例如,求 -5 的 8 位补码:

5   = 0000 0101
取反 = 1111 1010
加1 = 1111 1011  (即 -5 的补码)

从补码还原为原值

对于 n 位补码:

  • 如果最高位为 0,表示正数,直接转为十进制。

  • 如果最高位为 1,表示负数,方法:

    1. 将补码取反(所有位取反)。
    2. 加 1,得到原数值的绝对值。
    3. 加上负号。

示例:补码 1111 1011

取反:0000 0100
加1: 0000 0101  → 5
加上负号 → -5

补码的运算与溢出

加法(减法同理)

两个补码直接相加,结果的高位溢出自动丢弃(对于固定位数)。

示例(8 位):

  0000 0101  (+5)
+ 1111 1011  (-5)
= 0000 0000  (0)

溢出判断

  • 正+正 = 负 或 负+负 = 正 时,表示溢出。

  • 更严格:最高位进位 ≠ 次高位进位 时溢出。

补码运算实例:10 – 12

模拟计算机内部 8 位补码计算 10 - 12:

  1. 转换为补码
    10 为正数,补码 = 0000 1010

-12 的补码:

  • 12 原码:0000 1100

  • 取反得反码:1111 0011

  • 加 1 得补码:1111 0100

  1. 加法运算
  0000 1010   (10)
+ 1111 0100   (-12)
= 1111 1110   (结果补码)
  1. 结果解读

结果补码 1111 1110 最高位为 1,表示负数。
求其真值:

  • 减 1:1111 1110 - 1 = 1111 1101

  • 取反:0000 0010 → 十进制 2

  • 加上负号 → -2

所以 10 - 12 = -2,正确。

Python 中的整数与补码

Python 的整数是任意精度的,没有固定的位数,因此不直接使用补码表示。但当进行位运算(如 &、|、^、~、<<、>>)时,Python 会模拟无限位宽的补码行为,即负数的二进制表示被视为其补码的无限扩展。

例如:

print(bin(5))          # 0b101
print(bin(-5))         # -0b101(注意:Python 的 bin 输出直接加负号)
print(~5)              # -6(按位取反)

我们可以用以下方法模拟固定位宽的补码表示:

def to_8bit_twos_complement(x):
    return x & 0xFF    # 取低 8 位(自动处理负数的补码)

print(to_8bit_twos_complement(-5))   # 251  (即 0b11111011)

总结

编码方式 正数表示 负数表示 范围(n 位) 零的个数
原码 符号位 + 绝对值 符号位 + 绝对值 -(2^(n-1)-1) ~ 2^(n-1)-1 两个
反码 同原码 符号位 + 数值位取反 同上 两个
补码 同原码 反码 +1 -2^(n-1) ~ 2^(n-1)-1 一个

补码之所以成为现代计算机的标准,是因为它:

  • 统一了加减法运算。

  • 简化了硬件设计。

  • 消除了 ±0 的歧义。

  • 多表示一个负数,范围更平衡。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐