1. 为什么需要编码?
计算机底层只能表示0和1,需要一种编码方式来表示负数和执行减法运算。 直接使用原码进行减法,电路设计会非常复杂。补码的出现,成功地将减法运算转换为加法运算,大大简化了CPU的算术逻辑单元(ALU)设计。
2. 核心概念与定义(以8位有符号整数为例)
我们假设用一个字节(8位)来表示一个数。最高位(最左边)为符号位,其余为数值位。
- 符号位: 0 表示正数,1 表示负数。
2.1 原码 (Sign-Magnitude)
- 定义: 最高位表示符号,其余位表示该数的绝对值的二进制形式。
- 范围: -127 到 +127 (即
1111 1111到0111 1111) - 特点: 直观,但存在 +0 和 -0 两种零的表示。
+0:0000 0000-0:1000 0000
示例:
- +5 = 0000 0101
- -5 = 1000 0101
2.2 反码 (Ones' Complement)
- 定义:
- 正数的反码与其原码相同。
- 负数的反码是其对应正数的原码按位取反(包括符号位)。
- 范围: -127 到 +127
- 特点: 同样存在 +0 和 -0 的问题。
+0:0000 0000-0:1111 1111
示例:
- +5 = 0000 0101 (原码) -> 0000 0101 (反码)
- -5:
1. +5的原码: 0000 0101
2. 按位取反: 1111 1010
3. 所以 -5的反码 = 1111 1010
2.3 补码 (Two's Complement) - 现代计算机标准
- 定义:
- 正数的补码与其原码相同。
- 负数的补码是其反码 + 1。
- 范围: -128 到 +127 (这是关键优势,比原码和反码多表示一个数)
- 特点: 解决了 ±0 的问题,统一了零的表示 (
0000 0000),并且减法可以变加法。
示例:
- +5 = 0000 0101 (原码) -> 0000 0101 (补码)
- -5:
1. +5的原码: 0000 0101
2. 按位取反得到反码: 1111 1010
3. 反码 + 1 得到补码: 1111 1011
所以 -5的补码 = 1111 1011
3. 计算与运算方式
3.1 由负数求其补码(快速方法)
- 写出该负数绝对值的原码。
- 从右向左,遇到第一个
1之前,保持所有位不变。 - 第一个
1之后,将其左边的所有位按位取反。
示例: 求 -12 的补码 (8位)
1. +12的原码: 0000 1100
2. 从右向左,第一个1在从右数第3位:0000 11**00**
3. 这个1左边的位全部取反:1111 01**00**
4. 结果: 1111 0100 (与 反码+1 方法结果一致)
3.2 由补码求其十进制值(快速方法)
- 如果符号位是
0,则为正数,直接转换。 - 如果符号位是
1,则为负数。 - 对该补码再次求补码(即按位取反再加1),得到的结果就是其绝对值的原码,然后加上负号。
示例: 求补码 1111 0100 的十进制值
1. 符号位是1,所以是负数。
2. 对 1111 0100 求补码:
- 按位取反: 0000 1011
- 加1: 0000 1100
3. 0000 1100 = 12
4. 所以原值是 -12
3.3 补码的加法运算
规则: 直接按二进制相加,包括符号位。忽略最高位的进位。
示例1: 7 + (-5)
- 7的补码: 0000 0111
- -5的补码: 1111 1011
- 相加:
```
0000 0111
+ 1111 1011
1 0000 0010
``
- **忽略溢出的进位**,得到0000 0010` = 2 ✅
示例2: 5 + (-3)
- 5的补码: 0000 0101
- -3的补码: 1111 1101
- 相加:
```
0000 0101
+ 1111 1101
1 0000 0010
``
- 忽略进位,得到0000 0010` = 2 ✅
3.4 补码的减法运算
规则: A - B = A + (-B)。即,将减数B求负(求其补码),然后与被减数A相加。
示例: 8 - 3
- 8的补码: 0000 1000
- 3的补码: 0000 0011
- -3的补码: 1111 1101
- 计算 8 + (-3):
```
0000 1000
+ 1111 1101
1 0000 0101
``
- 忽略进位,得到0000 0101` = 5 ✅
4. 特殊情况
4.1 溢出 (Overflow)
当两个正数相加得到负数,或两个负数相加得到正数时,发生了溢出。结果是不正确的。
- 判断方法: 只有当两个加数的符号位相同,且结果的符号位与它们不同时,才发生溢出。
- 示例: 120 + 10 (8位)
- 120补码: 0111 1000
- 10补码: 0000 1010
- 相加: 0111 1000 + 0000 1010 = 1000 0010 (结果是 -126,显然是错误的,因为130 > 127)
- 判断: 两个正数(符号0)相加,结果符号位为1(负数),溢出发生。
4.2 最小负数(没有原码和反码)
在8位补码中,范围是 -128 到 +127。
- -128的补码是 1000 0000。
- 特殊点: -128 这个数没有原码和反码,因为8位原码/反码的范围是 -127 到 +127。
- 对 1000 0000 求补码(按位取反得 0111 1111,加1得 1000 0000),还是它自己。这解释了为什么它是 -128。
4.3 零的表示
- 原码/反码: 有
+0和-0两种表示,这在进行相等判断时很麻烦。 - 补码: 唯一的零,表示为
0000 0000。这是补码的一大优势。
总结
| 特性 | 原码 | 反码 | 补码 |
|---|---|---|---|
| 正数表示 | 与二进制相同 | 与原码相同 | 与原码相同 |
| 负数表示 | 符号位1+绝对值 | 正数原码按位取反 | 反码 + 1 |
| 零的表示 | +0 和 -0 | +0 和 -0 | 唯一的0 |
| 表示范围(8位) | -127 ~ +127 | -127 ~ +127 | -128 ~ +127 |
| 减法操作 | 复杂 | 较复杂 | 转换为加法 |
| 硬件实现 | 复杂 | 较复杂 | 简单高效 |
结论: 由于补码解决了零的歧义、统一了加减法运算、扩大了表示范围,并且硬件实现简单,因此现代计算机一律使用补码来表示和存储有符号整数。
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com