第20章 数据编码
| 等级 | 知识块 | 考查频率 |
|---|---|---|
| GESP C++ 三级 | 数据编码(原码、反码、补码) | ⭐⭐⭐ |
计算机的硬件电路只认 0 和 1。数值在计算机中以二进制形式存储,而原码、反码、补码是表示「有符号整数」的三种编码方式。它们的设计目的是:解决负数的表示与运算问题,尤其是用加法器实现减法。本章讲清三种编码的定义、转换规则与运算,并配合 C++ 示例验证。
20.1 机器数与真值
- 机器数:一个数在计算机中的二进制表示形式,包含符号位和数值位。通常用最高位表示符号(0 为正,1 为负),其余位表示数值的绝对值。
- 例:8 位机器数
00000101和10000101。
- 例:8 位机器数
- 真值:机器数对应的实际数值。
00000101的真值是 +5,10000101的真值是 −5。
直接用「原码」做减法非常麻烦(要先判断符号再决定加减),而且 0 有两种表示(+0 与 −0)。补码的出现统一了加减法、消除了 ±0 的歧义,因此现代计算机内部一律用补码存储有符号整数。
20.2 原码(True Form)
20.2.1 定义
- 正数:符号位为 0,数值位为该数二进制绝对值。
- 负数:符号位为 1,数值位为该数绝对值的二进制。
20.2.2 表示方法(以 8 位为例:1 位符号位 + 7 位数值位)
- 真值 +5 的原码:
00000101 - 真值 −5 的原码:
10000101 - 0 有两种形式:
+0 = 00000000,−0 = 10000000
20.2.3 特点
- 直观、与真值对应简单;
- 加减法复杂(需先判符号);
- 存在两种 0 的编码,浪费一个状态。
20.3 反码(One's Complement)
20.3.1 定义
- 正数:反码 = 原码。
- 负数:符号位保持 1,数值位按位取反(0 变 1,1 变 0)。
20.3.2 表示方法(8 位)
- +5:原码
00000101,反码同为00000101; - −5:原码
10000101→ 数值位取反 → 反码11111010; - 0 仍有两种形式:
+0 = 00000000,−0 = 11111111。
20.3.3 特点
- 正数原反码一致;
- 仍存在双零问题;
- 加法规则特殊:若最高位产生进位,需把进位循环加回最低位。
示例:计算
- +5 反码:
00000101 - −3 原码
10000011→ 反码11111100 - 相加:
00000101 + 11111100 = 100000001(9 位) - 循环进位:把最高位的 1 加到末尾 →
00000001 + 1 = 00000010,真值为 2。
反码的「循环进位」仍不够优雅,因此工程上几乎不用它做运算,它主要是求补码的中间步骤。
20.4 补码(Two's Complement)
补码是计算机标准的有符号整数编码,彻底解决了双零和减法复杂的问题。
20.4.1 定义
- 正数:补码 = 原码 = 反码;
- 负数:补码 = 反码 + 1(无循环进位)。
20.4.2 表示方法(8 位)
- +5:
00000101 - −5:原码
10000101→ 反码11111010→ 补码11111011 - 0 只有唯一编码:
00000000
20.4.3 n 位补码的取值范围
n 位(1 符号位 + n−1 数值位):
8 位范围:−128 ~ 127。10000000 专门表示 −128,它没有对应的原码/反码。
20.4.4 运算规则
- 加法:,直接相加,最高位进位直接丢弃(模 )。
- 减法:,减法转为「加负数的补码」。
- 求 的补码:对 全部位取反再加 1。
示例1:
- +5 补码
00000101,−3 补码11111101 - 相加:
00000101 + 11111101 = 100000010,舍弃进位得00000010,真值 2。
示例2:
- +3 补码
00000011,−5 补码11111011 - 相加得
11111110(负数补码),还原:补码 −1 得反码11111101,数值位取反 → 原码10000010,真值 −2。
20.4.5 补码优势
- 0 唯一,节省一个状态;
- 减法完全转为加法,硬件只需一个加法器;
- 比原码多表示一个最小负数(−128),取值范围更大。
20.5 三种编码转换关系
20.5.1 正数
20.5.2 负数
- 原码 → 反码:符号位不变,数值位取反;
- 反码 → 补码:整体 +1;
- 补码 → 原码:两种方式等价
- 补码全部位取反加 1;
- 补码 −1 得到反码,再数值位取反(符号位不变)。
示例:X = −5(8 位)
- 原码:
10000101 - 反码:
11111010 - 补码:
11111011 - 补码求原码:
11111011取反00000100+1 →00000101,加回符号位 →10000101。
负数三码转换:原 → 反(数值取反)→ 补(+1);反向同样「取反 +1」即可(补码再取反 +1 回到原码)。记住 −5 的补码 11111011 这种典型值,考试直接推。
20.6 补码溢出
运算结果超出 n 位补码范围即为溢出,结果不可信。
20.6.1 判断方法(单符号位)
- 两个正数相加,结果符号位变成 1 → 正溢出;
- 两个负数相加,结果符号位变成 0 → 负溢出;
- 一正一负相加永远不会溢出。
示例:8 位计算 120 + 10
- 120 补码
01111000,10 补码00001010 - 相加得
10000010,符号位变为 1 → 正溢出(130 > 127)。
最高位产生的「进位」在补码加法里是正常丢弃的;溢出是指结果符号错误(两个同号数加出异号)。判断是否溢出看「符号位」,不是看有没有进位。
20.7 计算机选用补码的原因
- 硬件简化:只需加法电路,减法转换加法;
- 零统一:消除 ±0 两种编码;
- 空间充分:多表示一个负极值,存储利用率更高。
20.8 C++ 验证示例
下面用 C++ 打印一个负数在内存中的补码,并验证 ~x = -x - 1 这条重要性质。
#include <iostream>
using namespace std;
// 以 8 位二进制形式输出一个 signed char 的补码
void printBinary(signed char x) {
for (int i = 7; i >= 0; i--) {
cout << ((x >> i) & 1); // 逐位取出,右移后&1
}
}
int main() {
signed char a = -5;
cout << "十进制 -5 在内存中的补码: ";
printBinary(a); // 输出 11111011
cout << endl;
// 验证补码性质:~x = -x - 1
cout << " ~a = " << (int)(~a)
<< " , -a - 1 = " << (int)(-a - 1) << endl;
return 0;
}
11111011 正是 −5 的补码;~a = 4,-a - 1 = 4,与公式一致。说明在补码体系下,~ 取反等价于「相反数减一」。
20.9 选择题练习
以下题目贴近 GESP 三级单选题风格。每道题下方附有折叠的参考答案与解析,点击即可展开对照。
1. 在 8 位补码表示中,能表示的整数范围是?
A. −127 ~ 127
B. −128 ~ 127
C. −128 ~ 128
D. 0 ~ 255
点击展开参考答案与解析
答案:B
解析:n 位补码范围 ,8 位即 −128 ~ 127,选 B。注意 −128 由 10000000 专门表示,没有对应的原/反码。
2. 十进制数 −5 的 8 位原码是?
A. 10000101
B. 11111010
C. 11111011
D. 00000101
点击展开参考答案与解析
答案:A
解析:负数原码:符号位 1 + 数值位绝对值二进制(5 = 0000101),即 10000101,选 A。B 是反码,C 是补码。
3. 关于补码,下列说法正确的是?
A. 0 在补码中有两种表示(+0 与 −0)
B. 负数的补码等于其反码减 1
C. 补码体系下减法可以转化为加法
D. 8 位补码无法表示 −128
点击展开参考答案与解析
答案:C
解析:补码中 0 唯一(A 错);负数补码 = 反码 +1(B 错);8 位补码能表示 −128(D 错)。补码的最大优势正是把减法转为加法,选 C。
4. 两个正整数在 8 位补码下相加,结果符号位变为 1,这说明?
A. 发生了正常进位,结果正确
B. 发生了溢出,结果错误
C. 发生了借位
D. 无任何异常
点击展开参考答案与解析
答案:B
解析:两正数相加结果却为负(符号位 1),属于正溢出,结果错误,选 B。
5. 已知 8 位补码 11110110,其对应的真值是?
A. −10
B. −9
C. −8
D. 246
点击展开参考答案与解析
答案:A
解析:11110110 是负数补码,求原码:取反 00001001 +1 → 00001010 = 10,加符号位 → −10,选 A。也可用 ~x+1 思路:补码减 1 得 11110101,取反 00001010 = 10,故 −10。