Python 原码、反码、补码详解
原码、反码、补码详解
计算机底层使用二进制存储数据,但并非直接存储数值对应的二进制形式,而是存储其补码。理解原码、反码、补码是掌握计算机整数运算的基础。
机器数与真值
机器数
一个数在计算机中的二进制表示形式称为机器数。机器数通常用最高位表示符号:
-
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,得到原数值的绝对值。
- 加上负号。
示例:补码 1111 1011
取反:0000 0100
加1: 0000 0101 → 5
加上负号 → -5
补码的运算与溢出
加法(减法同理)
两个补码直接相加,结果的高位溢出自动丢弃(对于固定位数)。
示例(8 位):
0000 0101 (+5)
+ 1111 1011 (-5)
= 0000 0000 (0)
溢出判断
-
正+正 = 负 或 负+负 = 正 时,表示溢出。
-
更严格:最高位进位 ≠ 次高位进位 时溢出。
补码运算实例:10 – 12
模拟计算机内部 8 位补码计算 10 - 12:
- 转换为补码
10 为正数,补码 = 0000 1010
-12 的补码:
-
12 原码:0000 1100
-
取反得反码:1111 0011
-
加 1 得补码:1111 0100
- 加法运算
0000 1010 (10)
+ 1111 0100 (-12)
= 1111 1110 (结果补码)
- 结果解读
结果补码 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 的歧义。
-
多表示一个负数,范围更平衡。
更多推荐



所有评论(0)