四种有符号数编码方式
1. 原码 (Sign-Magnitude)
核心思想:最高位表示符号(0正1负),其余位表示绝对值。
- 正数:符号位0,数值位为真值的二进制绝对值
- 负数:符号位1,数值位为真值的二进制绝对值
例子(8位表示):
+18→ 原码:0001 0010-18→ 原码:1001 0010
特点:
- 直观易懂,符合人类习惯
- 存在+0和-0 :
0000 0000和1000 0000都表示0 - 加减运算复杂:需要判断符号位,硬件实现麻烦
2. 反码 (Ones' Complement)
核心思想:正数的反码等于原码,负数的反码是原码的符号位不变,其余位按位取反。
- 正数:与原码相同
- 负数:符号位保持1,数值位全部取反
例子(8位表示):
+18→ 原码:0001 0010→ 反码:0001 0010-18→ 原码:1001 0010→ 反码:1110 1101
特点:
- 存在+0和-0 :
0000 0000和1111 1111都表示0 - 加减法稍微简化,但仍有循环进位问题
- 补码出现前的过渡方案,现已基本不用
3. 补码 (Two's Complement) ✅ 最重要!现代计算机标准
核心思想:正数的补码等于原码,负数的补码等于反码加1。
- 正数:与原码相同
- 负数:符号位保持1,数值位取反后加1
例子(8位表示):
-
+18→ 原码:0001 0010→ 补码:0001 0010 -
-18:原码:1001 0010 取反:1110 1101 (反码) 加1:1110 1110 (补码)
特点:
- 只有一个0 :
0000 0000 - 范围不对称 :n位补码范围是
-2ⁿ⁻¹到+(2ⁿ⁻¹ - 1)- 8位:-128 到 +127
- 加减运算统一:减法可转换为加法,硬件实现简单
- 现代计算机几乎全部使用补码
快速求负法:从右向左找到第一个1,这个1及其右边的位保持不变,左边的位全部取反。
-18的补码1110 1110→ 从右第一个1在倒数第二位,保留10,左边取反得1110 11,即1110 1110
4. 移码 (Biased Representation / Excess-K)
核心思想:真值加上一个固定偏移量K(通常K=2ⁿ⁻¹),使其全部变为非负数。
- 公式:移码 = 真值 + 2ⁿ⁻¹
- 等价关系:移码 = 补码的符号位取反
例子(8位,偏移量K=128):
+18→ 补码:0001 0010→ 移码:1001 0010(符号位取反)-18→ 补码:1110 1110→ 移码:0110 1110(符号位取反)0→ 补码:0000 0000→ 移码:1000 0000(=128)
特点:
- 所有值都是非负的,便于比较大小
- 主要用于浮点数的阶码(指数部分)
- 比较两个移码数的大小,就是比较它们对应的真值的大小
综合对比(以4位二进制为例)
| 真值 | 原码 | 反码 | 补码 | 移码(K=8) |
|---|---|---|---|---|
| +7 | 0111 | 0111 | 0111 | 1111 |
| +6 | 0110 | 0110 | 0110 | 1110 |
| +5 | 0101 | 0101 | 0101 | 1101 |
| +4 | 0100 | 0100 | 0100 | 1100 |
| +3 | 0011 | 0011 | 0011 | 1011 |
| +2 | 0010 | 0010 | 0010 | 1010 |
| +1 | 0001 | 0001 | 0001 | 1001 |
| +0 | 0000 | 0000 | 0000 | 1000 |
| -0 | 1000 | 1111 | - | - |
| -1 | 1001 | 1110 | 1111 | 0111 |
| -2 | 1010 | 1101 | 1110 | 0110 |
| -3 | 1011 | 1100 | 1101 | 0101 |
| -4 | 1100 | 1011 | 1100 | 0100 |
| -5 | 1101 | 1010 | 1011 | 0011 |
| -6 | 1110 | 1001 | 1010 | 0010 |
| -7 | 1111 | 1000 | 1001 | 0001 |
| -8 | - | - | 1000 | 0000 |
关键区别总结
| 特性 | 原码 | 反码 | 补码 | 移码 |
|---|---|---|---|---|
| 0的表示 | 两种(+0/-0) | 两种(+0/-0) | 一种 | 一种(非零值) |
| 表示范围(4位) | -7 ~ +7 | -7 ~ +7 | -8 ~ +7 | -8 ~ +7(但编码全是正数) |
| 加减运算 | 复杂,需判断符号 | 较简单,但有循环进位 | 最简单,统一为加法 | 用于比较,不做加减 |
| 硬件实现 | 复杂 | 较简单 | 最简单 | 简单 |
| 主要用途 | 早期计算机/浮点数尾数 | 历史过渡 | 现代整数运算标准 | 浮点数阶码 |
| 符号处理 | 最高位=符号 | 最高位=符号 | 最高位=符号 | 无符号(全是正数编码) |
| 数值连续性 | 不连续(有±0) | 不连续(有±0) | 连续 | 连续 |
为什么补码成为标准?
- 运算统一性 :
A - B = A + (-B),减法器不需要了 - 硬件简化:只需要加法器和补码转换电路
- 唯一零表示:避免歧义和硬件复杂度
- 表示范围更合理:比原码/反码多表示一个数(-2ⁿ⁻¹)
实际应用场景
- CPU中的整数:全部使用补码表示和运算
- 浮点数(IEEE 754) :
- 尾数部分:用原码表示(有符号绝对值)
- 阶码部分:用移码表示(便于比较指数大小)
快速记忆技巧
- 原码:最直观,看符号位就知道正负
- 反码:"反"着来,负数就是取反
- 补码:负数=反码+1,现代计算机的王者
- 移码:给真值加个偏移量,全变正数,浮点数用它
这四种编码方式共同构成了计算机处理有符号数的理论基础,其中补码无疑是最重要、最核心的概念。
5.原码和以及反码,补码,移码能表示的整数数和小数范围
| 编码方式 | 整数范围 | 小数范围(定点) | 零的表示 |
|---|---|---|---|
| 原码 | -(2ⁿ⁻¹ - 1) ~ +(2ⁿ⁻¹ - 1) 例(8位): -127 ~ +127 | -(1-2⁻⁽ⁿ⁻¹⁾) ~ +(1-2⁻⁽ⁿ⁻¹⁾) 例(8位): -0.992 ~ +0.992 | +0 和 -0 两个零 |
| 反码 | -(2ⁿ⁻¹ - 1) ~ +(2ⁿ⁻¹ - 1) 例(8位): -127 ~ +127 | -(1-2⁻⁽ⁿ⁻¹⁾) ~ +(1-2⁻⁽ⁿ⁻¹⁾) 例(8位): -0.992 ~ +0.992 | +0 和 -0 两个零 |
| 补码 | -2ⁿ⁻¹ ~ +(2ⁿ⁻¹ - 1) 例(8位): -128 ~ +127 | -1.0 ~ +(1-2⁻⁽ⁿ⁻¹⁾) 例(8位): -1.0 ~ +0.992 | 唯一零 |
| 移码 | 真值: -2ⁿ⁻¹ ~ +(2ⁿ⁻¹ - 1) 编码: 0 ~ (2ⁿ - 1) 例(8位): 真值-128+127<br>编码0255 | 不用于小数表示 (主要用于浮点数阶码) | 唯一零 (编码为2ⁿ⁻¹) |
5.1为什么原码和反码范围相同?
5.1.1. 原码定义
原码 = 符号位 + 绝对值的二进制
- 符号位:0正1负
- 数值位:n-1位表示绝对值
最大正数 :0 111...111 = +(2^{n-1}-1)
最大负数 :1 111...111 = -(2^{n-1}-1)
5.2. 反码定义
反码 = 符号位 + 绝对值的按位取反(负数时)
- 正数:与原码相同
- 负数:绝对值按位取反
以4位为例:
十进制 原码 反码
+3 0011 0011 ← 正数相同
-3 1011 1100 ← 负数取反
关键 :反码的负数表示范围与原码完全一致:
- 最小的负数模式:
1 000...000在原码中是 -0,在反码中也是 -0 - 最大的负数模式:
1 111...111在原码中是 -(2^{n-1}-1),在反码中也是 -(2^{n-1}-1)
因为原码和反码都是1位符号号+其它数值位
5.2 为什么补码比原码/反码多一个负数?
根本原因:补码重新定义了 100...000 的含义
| 二进制模式 | 原码含义 | 反码含义 | 补码含义 |
|---|---|---|---|
| 100...000 | -0(无效/视为0) | -0(无效/视为0) | -2^{n-1}(有效负数) |
补码的巧妙设计
补码中,-0的编码被重新利用来表示多一个负数:
4位时的对比:
| 十进制 | 原码 | 反码 | 补码 |
|---|---|---|---|
| -8 | 无法表示 | 无法表示 | 1000 |
| -7 | 1111 | 1000 | 1001 |
| -6 | 1110 | 1001 | 1010 |
| -5 | 1101 | 1010 | 1011 |
| -4 | 1100 | 1011 | 1100 |
| -3 | 1011 | 1100 | 1101 |
| -2 | 1010 | 1101 | 1110 |
| -1 | 1001 | 1110 | 1111 |
| -0 | 1000 | 1111 | 无 |
| +0 | 0000 | 0000 | 0000 |
补码"偷"了-0的编码1000来表示-8,所以负数范围多一个。
5.3 移码范围
5.3.1 补码 vs 移码(以4位为例)
| 二进制编码 | 补码真值 | 移码真值 | 移码编码值 |
|---|---|---|---|
| 0000 | 0 | -8 | 0 |
| 0001 | 1 | -7 | 1 |
| 0010 | 2 | -6 | 2 |
| 0011 | 3 | -5 | 3 |
| 0100 | 4 | -4 | 4 |
| 0101 | 5 | -3 | 5 |
| 0110 | 6 | -2 | 6 |
| 0111 | 7 | -1 | 7 |
| 1000 | -8 | 0 | 8 |
| 1001 | -7 | 1 | 9 |
| 1010 | -6 | 2 | 10 |
| 1011 | -5 | 3 | 11 |
| 1100 | -4 | 4 | 12 |
| 1101 | -3 | 5 | 13 |
| 1110 | -2 | 6 | 14 |
| 1111 | -1 | 7 | 15 |
5.3.2 关键区别解析
1. 编码范围不同
- 补码编码 :1000(-8) ~ 0111(7)
从-8到7,共16个数 - 移码编码 :0000(0) ~ 1111(15)
从0到15,共16个数
2. 真值范围相同
- 两种编码都表示 -8到7 这16个整数
- 只是同一个真值的编码不同
5.3.3 移码的定义与计算
1. 移码公式
对于n位二进制:
\\text{移码} = \\text{真值} + 2\^{n-1}
或
\\text{移码} = \\text{补码} \\oplus 100...0 \\ (\\text{符号位取反})
2. 举例计算
真值 = -5
n=4,偏移量 = 2³ = 8
移码 = -5 + 8 = 3 = 0011
验证:
-5的补码 = 1011
符号位取反:1011 → 0011 ✓
5.3.3 为什么要有移码?
移码的主要优点:
-
简化比较运算
- 移码的真值越大,编码值也越大
- 可以直接用无符号比较器比较有符号数
-
浮点数阶码使用
- IEEE 754标准中,阶码用移码表示
- 便于处理正负指数
-
直观性
- 全0编码表示最小真值(-8)
- 全1编码表示最大真值(7)
对比示例:
比较两个数的大小:
- 补码比较 :-5(1011) vs 3(0011)
1011 > 0011(无符号),但-5 < 3(实际) - 移码比较 :-5(0011) vs 3(1011)
0011 < 1011(无符号),且-5 < 3(实际一致)