1 机器数与真值
计算机硬件仅能存储由 0 和 1 组成的二进制序列,该序列本身不携带正负号信息。为了在计算机中表示带符号的整数,底层硬件必须采用特定的编码规则。在理解这些编码规则之前,你需要先区分两个基础概念:机器数与真值。
1.1 机器数
机器数(Machine Number)是指数值在计算机底层硬件中实际存储的二进制编码序列。
同一个二进制序列在不同解释方式下会对应不同的数值。对于整数类型,其解释方式分为以下两类:
- 无符号解释:将二进制序列中的全部位均视为数值位,每一位按权展开后求和。该解释方式下不存在正负号,所得结果始终为非负整数。
- 有符号解释:将二进制序列映射为可正可负的数值。该解释方式约定最高位为符号位标志(0 表示正数,1 表示负数),但其余数值位的加权方式取决于具体的编码规则。有符号解释必须依赖具体的编码规则,不同规则会将同一个机器数映射为不同的数值。
1.2 真值
真值(True Value)是指机器数所代表的实际数学数值,即你日常书写中带正负号的十进制数(如 +520、-666)。
机器数转换为真值,取决于所采用的解释方式:
- 若采用无符号解释,将所有位按权展开后求和,所得结果即为该机器数的真值。
- 若采用有符号解释,则必须依据具体的编码规则进行映射转换。在未明确编码规则之前,仅凭二进制序列本身无法唯一确定其有符号真值。
以 8 位机器数 1000 0001 为例:
- 若采用无符号解释,8 位全部参与按权展开求和,其真值为:1×2⁷ + 0×2⁶ + 0×2⁵ + 0×2⁴ + 0×2³ + 0×2² + 0×2¹ + 1×2⁰ = 129。
- 若采用有符号解释,此时最高位的 1 表示该数为负数,但其余 7 位(000 0001)的加权值取决于具体编码规则。因此,要得出该机器数在有符号解释下的真值,必须先明确其采用的编码规则。
2 原码、反码与补码
上一节提到,有符号解释必须依赖具体的编码规则。常见的编码规则主要有三种:原码、反码与补码。这三者分别代表了有符号整数编码从直观实现到现代标准的不同演进阶段。
2.1 原码:符号位加数值位
原码(Sign-Magnitude Representation)是最直观的有符号整数表示方法。其编码方式与人类对正负数的日常书写习惯一致,采用 “符号位加数值位” 的结构。
编码规则
一个 n 位二进制数的原码由以下两部分组成:
- 符号位:位于最高位。0 表示正数,1 表示负数。
- 数值位:其余 n - 1 位,表示该数绝对值的二进制形式。
典型示例
| 真值 | 原码表示(8 位) | 说明 |
|---|---|---|
| +1 | 0000 0001 | 符号位为 0,数值位为 1 |
| -1 | 1000 0001 | 符号位为 1,数值位为 1 |
| +0 | 0000 0000 | 符号位与数值位全为 0 |
| -0 | 1000 0000 | 符号位为 1,数值位全为 0 |
| +127 | 0111 1111 | 8 位原码的最大值 |
| -127 | 1111 1111 | 8 位原码的最小值 |
特点与局限
- 直观易懂:原码采用 “符号位加数值位” 的结构,符合人类对正负数的自然理解。
- 零的冗余:存在 0000 0000(+0)和 1000 0000(-0)两种表示形式,造成编码空间浪费。
💡 提示:原码的定位
原码主要作为理解有符号数编码演进的起点,而非实际用于计算机的算术运算。
2.2 反码:原码到补码的过渡
反码(One's Complement)是一种早期的编码方式,其核心思想是为负数引入 “按位取反” 操作,为补码的诞生提供了过渡思路。
编码规则
- 正数的反码:与原码相同。
- 负数的反码:符号位保持为 1 不变,数值位逐位取反(0 变 1,1 变 0)。
典型示例
| 真值 | 反码表示(8 位) | 说明 |
|---|---|---|
| +1 | 0000 0001 | 正数反码与原码相同 |
| -1 | 1111 1110 | 符号位为 1,原码数值位 000 0001 取反得 111 1110 |
| +0 | 0000 0000 | 正零的反码仍为全 0 |
| -0 | 1111 1111 | 原码 1000 0000 的数值位全 0 取反得全 1 |
| +127 | 0111 1111 | 8 位反码的最大值 |
| -127 | 1000 0000 | 原码 1111 1111 的数值位全 1 取反得全 0 |
特点与局限
- 过渡作用:反码是理解补码生成过程的关键中间步骤。
- “双零” 问题:仍存在 0000 0000(+0)和 1111 1111(-0)两种表示。
💡 提示:反码的历史定位
反码为 “取反加一” 操作提供了铺垫,但本身未被现代计算机广泛采用。
2.3 补码:现代计算机的标准
补码(Two's Complement)是现代计算机系统中最广泛采用的有符号整数表示方法。它通过统一的编码规则,彻底解决了原码和反码中的 “双零” 问题。
编码规则
- 正数的补码:与原码相同。
- 负数的补码:先得到其原码,符号位保持为 1 不变,数值位逐位取反(得到反码),再在最低位加 1。整个操作过程通常概括为“取反加一”。
典型示例
| 真值 | 补码表示(8 位) | 说明 |
|---|---|---|
| +1 | 0000 0001 | 正数补码与原码相同 |
| -1 | 1111 1111 | 原码 1000 0001 → 反码 1111 1110 → 补码 1111 1111 |
| 0 | 0000 0000 | 正零与负零的补码均为 0000 0000; 负零 1000 0000 取反加一后溢出,高位舍弃; 零的表示唯一 |
| +127 | 0111 1111 | 8 位补码的最大值 |
| -128 | 1000 0000 | 8 位补码的最小值,无对应的原码或反码 |
编码特性
- 零表示唯一:消除了原码和反码中 +0 与 -0 的冗余,所有零均统一为 0000 0000。
- 范围非对称:在 n 位补码中,取值范围为 -2^(n-1) 到 2^(n-1) - 1。对于 8 位系统,范围为 -128 到 +127,-128 是补码独有的最小值,没有对应的原码或反码。
💡 提示:补码的统治地位
补码是现代计算机(如 x86、ARM 架构)和主流编程语言(如 C、C++、Java、Python)的标准整数编码方式。掌握其原理是理解计算机算术运算的基础。
2.4 三种编码对比
本节将原码、反码、补码三种编码方式放在一起进行对比,便于你从整体上把握它们的异同。
规则对比
- 正数
- 正数的原码、反码、补码三者完全相同。对于任意正数,三种编码的二进制表示均一致。
- 负数
- 原码:符号位为 1,数值位为绝对值的二进制形式。
- 反码:在原码基础上,符号位不变,数值位逐位取反。
- 补码:在反码基础上,在最低位加 1(即 “取反加一”)。
- 零
- 原码与反码:均存在 +0 与 -0 两种表示。
- 补码:+0 与 -0 合并为唯一的0000 0000。
综合示例
| 数值 | 原码(8 位) | 反码(8 位) | 补码(8 位) | 说明 |
|---|---|---|---|---|
| +1 | 0000 0001 | 0000 0001 | 0000 0001 | 正数三码合一 |
| -1 | 1000 0001 | 1111 1110 | 1111 1111 | 补码 = 反码 + 1 |
| +0 | 0000 0000 | 0000 0000 | 0000 0000 | 补码中零表示唯一 |
| -0 | 1000 0000 | 1111 1111 | ||
| +127 | 0111 1111 | 0111 1111 | 0111 1111 | 8 位三码的最大值 |
| -127 | 1111 1111 | 1000 0000 | 1000 0001 | 原码与反码的最小值 |
| -128 | 无法表示 | 无法表示 | 1000 0000 | 8 位补码独有的最小值 |
⚠️ 注意:补码的非对称范围
在 8 位补码中,取值范围的绝对值并不对称:最小值为 -128,最大值为 +127。这是因为补码将原码中表示 -0 的编码 1000 0000 重新映射为 -128,从而实现了编码空间的最大利用。-128 是 8 位补码中唯一一个没有对应原码和反码的特殊值。
3 补码的运算与存储优势
补码之所以能成为现代计算机系统的标准,不仅在于它解决了 “双零” 问题,更在于它在硬件运算和数据存储上带来的两大核心优势。
3.1 运算优势:统一加减法
补码的最大优势在于它将加法和减法统一为同一种运算。
在计算机硬件中,补码允许符号位和数值位一同参与加法运算,无需任何额外的符号判断。CPU 仅需一套加法电路即可完成所有整数加减操作,这极大地简化了算术逻辑单元(Arithmetic Logic Unit,简称 ALU)的设计。
原码运算的问题
在原码表示法中,硬件处理加减法时需遵循以下流程:
- 若两数同号:数值位做加法,符号位保持不变。
- 若两数异号:先比较两个数值位的大小,用绝对值较大的数减去绝对值较小的数,符号位取绝对值较大者的符号。
这套逻辑既需要加法电路,也需要减法电路,硬件实现较为复杂。
补码的解决方案
补码的设计使得符号位和数值位可以一起参与加法运算,硬件无需区分操作数的符号。以下通过计算2 + (-2)进行对比:
使用原码:
- +2 的原码为 0000 0010
- -2 的原码为 1000 0010
- 硬件直接相加:0000 0010 + 1000 0010 = 1000 0100
- 结果 1000 0100 是 -4 的原码,与期望结果 0 不符。
使用补码:
- +2 的补码为 0000 0010
- -2 的补码为 1111 1110
- 硬件直接相加:0000 0010 + 1111 1110 = 1 0000 0000
- 舍弃溢出的最高位(第 9 位)后,结果为 0000 0000,即 0 的补码,与期望结果一致。
上述对比表明,采用补码后,减法运算可转换为加法运算(加一个数的补码),硬件无需设计独立的减法器。这是补码被现代计算机广泛采用的核心原因之一。
3.2 存储优势:统一的位模式
补码的另一项核心优势在于:它允许有符号整数与无符号整数共享相同的物理存储格式。
在计算机内存中,无论是有符号整数还是无符号整数,均以相同长度的二进制位序列存储。二者的唯一区别在于程序如何解释这串二进制位:
- 无符号整数:所有位均视为数值位。
- 有符号整数(补码):最高位视为符号位,其余位按补码规则解释。
| 位模式(8 位) | 作为无符号整数 | 作为有符号整数(补码) |
|---|---|---|
| 0000 0000 | 0 | 0 |
| 0000 0001 | 1 | +1 |
| 0111 1111 | 127 | +127 |
| 1000 0000 | 128 | -128 |
| 1111 1110 | 254 | -2 |
| 1111 1111 | 255 | -1 |
补码的这一特性,使得有符号整数能够以最自然的方式融入现有的二进制存储体系,与无符号整数共享相同的物理表示,仅通过解释方式区分语义。这极大地简化了内存管理和类型系统的设计。
4 特殊补码模式
在实际应用中,某些特定位模式的补码具有固定的数学含义。掌握这些模式有助于你在阅读二进制数据时快速判断其对应的数值。
4.1 全 1 模式
在补码系统中,一个 n 位二进制数的所有位均为 1 时,其表示的真值恒为-1。
推导(以 8 位为例):
可以通过 -1 的补码生成过程来验证这一结论:
- 取 -1 的原码:1000 0001
- 求反码(符号位 1 不变,数值位 000 0001 逐位取反):1111 1110
- 末尾加 1:1111 1110 + 1 = 1111 1111
最终得到的 1111 1111 即为 8 位全 1 的二进制模式。这一结果是补码 “取反加一” 编码规则的直接推论。
常见位宽示例:
| 位宽 | 全 1 补码模式 | 对应真值 |
|---|---|---|
| 8 位 | 1111 1111 | -1 |
| 16 位 | 1111 1111 1111 1111 | -1 |
| 32 位 | 1111 1111 1111 1111 1111 1111 1111 1111 | -1 |
4.21 后全 0 模式
当一个 n 位补码数的最高位(符号位)为 1,其余 n - 1 位均为 0 时,它表示该位宽下能表示的最小负数,即-2^(n-1)。
常见位宽示例:
| 位宽 | 补码模式 | 对应真值 |
|---|---|---|
| 4 位 | 1000 | -2^3 = -8 |
| 8 位 | 1000 0000 | -2^7 = -128 |
| 16 位 | 1000 0000 0000 0000 | -2^15 = -32768 |
该模式具有一个重要特征:它是补码范围中唯一一个没有对应正数的值。
以 8 位系统为例,1000 0000 表示 -128,但其对应的正数 +128 无法在 8 位有符号整数中表示(8 位补码的最大正数为 +127)。因此,1000 0000 是唯一一个不能通过对某个正数 “取反加一” 得到的负数补码。这一特征的根源在于补码取值范围的非对称性 ——n 位补码的最小值为-2^(n-1),而最大值为2^(n-1) - 1,负数一侧的绝对值比正数一侧大 1。
5 原码与补码的相互转换
前文已讲解原码、反码与补码的编码规则。本节在此基础上,讲解负数在原码与补码之间相互转换的具体方法,并说明转换过程中可能遇到的边界情况。
5.1 原码转补码
将负数的原码转换为补码,遵循以下步骤:
- 符号位不变:原码的最高位为 1(表示负数),在补码中仍为 1。
- 数值位取反:将原码中除符号位外的所有位逐位取反(0 变 1,1 变 0)。
- 末位加 1:在取反后的结果上,在最低位加 1。
案例演示:将 -5 的 8 位原码转换为补码。
| 步骤 | 操作说明 | 二进制(8 位) |
|---|---|---|
| ① 原码 | -5 的 8 位原码 | 1000 0101 |
| ② 数值位取反 | 符号位 1 不变,数值位 000 0101 取反 | 1111 1010 |
| ③ 末位加 1 | 1111 1010 + 1 | 1111 1011 |
5.2 补码转原码
将负数的补码转换为原码,有两种等价的方法。
方法一:减 1 后取反(逆向操作法)
该方法与 “原码转补码” 的步骤完全相反,逻辑直观。
转换步骤:
- 符号位不变:补码的符号位 1 保留。
- 数值位减 1:将数值位整体减 1。
- 数值位取反:对减 1 后的结果逐位取反,得到原码的数值位。
案例演示:将 -5 的 8 位补码 1111 1011 转换为原码。
| 步骤 | 操作说明 | 二进制(8 位) |
|---|---|---|
| ① 补码 | 给定 -5 的 8 位补码 | 1111 1011 |
| ② 数值位减 1 | 低 7 位 111 1011 减 1 | 1111 1010 |
| ③ 数值位取反 | 低 7 位 111 1010 取反为 000 0101,符号位不变 | 1000 0101 |
方法二:取反后加 1(对称操作法)
该方法利用补码的 “对合性” 特征:对负数的补码再次执行 “数值位取反,末位加 1”,可还原其原码。
转换步骤:
- 符号位不变:符号位仍为 1。
- 数值位取反:将数值位逐位取反。
- 末位加 1:在取反结果的末位加 1,得到原码。
案例演示:将 -5 的 8 位补码 1111 1011 转换为原码。
| 步骤 | 操作说明 | 二进制(8 位) |
|---|---|---|
| ① 补码 | 给定 -5 的 8 位补码 | 1111 1011 |
| ② 数值位取反 | 低 7 位 111 1011 取反为 000 0100 | 1000 0100 |
| ③ 末位加 1 | 1000 0100 + 1 | 1000 0101 |
两种方法对比
| 对比维度 | 方法一:减 1 后取反 | 方法二:取反后加 1 |
|---|---|---|
| 操作顺序 | 先减 1,后取反 | 先取反,后加 1 |
| 本质 | “取反加 1” 的数学逆运算 | 利用补码的对合性,与正向操作完全一致 |
两种方法等价,结果完全一致。
5.3 特殊边界值
在 8 位补码系统中,补码1000 0000 表示 -128。-128 没有对应的原码(8 位原码最小只能表示 -127),因此无法通过 5.1 节的方法从原码转换,也无法通过 5.2 节的方法还原为原码。1000 0000 属于 8 位补码系统的特殊定义值。
对于 n 位系统,对应的特殊边界值为-2^(n-1),其补码形式为 1 后跟 n-1 个 0。
尝试对 1000 0000 执行 “减 1 后取反” 操作:
- 数值位 000 0000 减 1 → 111 1111(发生借位)
- 111 1111 取反 → 000 0000
- 结果仍为 1000 0000
尝试对 1000 0000 执行 “取反后加 1” 操作:
- 数值位 000 0000 取反 → 111 1111
- 加 1 → 1 0000 0000(最高位溢出舍弃)
- 结果仍为 1000 0000
两次尝试均未能改变 1000 0000,结果仍为自身。这表明在 8 位补码系统中,-128 是无法通过 “取反加 1” 或 “减 1 后取反” 流程还原的特殊值。
6 三码快速转换工具
在学习原码、反码与补码的过程中,手动计算耗时长、易出错,尤其在处理负数和边界值时更为明显。借助工具可实现快速、准确的转换,是验证手动计算结果的有效辅助手段。
6.1 使用 Windows 计算器
Windows 系统自带的 “计算器” 应用提供了程序员模式,可直接查看整数在不同进制下的补码表示。
打开计算器并切换至 “程序员” 模式后,在 “DEC”(十进制)模式下输入一个整数,其 “BIN”(二进制)一栏显示的就是该整数在当前位宽下的补码。
以 8 位(BYTE)位宽为例,输入-1,计算器显示结果如下:
-1 的补码为 1111 1111,与你在 4.1 节中学习的 “全 1 模式表示 -1” 完全一致。
6.2 在线转换工具
除了系统自带的计算器,你还可以使用在线工具进行快速验证。
工具一:在线原码 / 反码 / 补码计算器
该工具的访问地址为:https://www.lddgo.net/convert/number-binary-code
该工具支持 4 位、8 位、16 位、32 位、64 位、128 位以及任意自定义位宽的原码、反码、补码计算。
以-5为例,先选择位宽为 8,然后在 “整数” 输入框中输入 -5,最后点击 “计算” 按钮,下方将同时显示其原码、反码和补码:
工具二:三贝计算网
该工具的访问地址为:https://www.23bei.com/tool/56.html
该工具同样支持二进制、十进制、十六进制数值的原码、反码、补码计算。
以-8为例,在输入框中输入 -8,在 “变量类型” 处选择 “原码(10 进制)”(表示输入的是十进制数),点击 “计算” 按钮,上方将显示其原码、反码和补码的二进制与十六进制形式: