---
title: 原码、反码、补码
createTime: 2026/01/08 16:34:05
permalink: /archives/6f41cabe-41e6-4a09-9f1c-af7dd709a35d/
tags:
  - cs-fundamentals
---

欢迎来到 0 与 1 的魔法派对！这篇文章将带你揭开原码、反码与补码的奥秘，看计算机如何巧妙地用补码化减为加。让我们一起翻开这页，去捕捉二进制底层那份迷人的理性之美吧♪

<!-- more -->

## 一、 前置概念

计算机底层存储数据的时候使用的是二进制数字，但是计算机在存储一个数字的时候并不是直接存储该数字对应的二进制数，而是存储该数字对应的**二进制数的补码**。

在了解原码、反码和补码之前，我们要了解**机器数**和**真值**的概念。

### 1) 机器数
一个数在计算机的存储形式是二进制数，我们称这些二进制数为**机器数**。机器数是有符号的，在计算机中用机器数的最高位存放符号位，`0` 表示正数，`1` 表示负数。

### 2) 真值
因为机器数带有符号位，所以机器数的形式值不等于其真实表示的值（真值）。
- 以机器数 `1000 0001` 为例，其真正表示的值（首位为符号位）为 `-1`，而形式值（首位就是代表 1）为 `129`。
- 因此将带符号的机器数的真正表示的值称为机器数的**真值**。

## 二、 原码、反码与补码

### 1) 原码
原码的表示与机器数真值表示的一样，即用第一位表示符号，其余位表示数值。
- **正数**：就是它对应的二进制数。
- **负数**：将绝对值对应的二进制最左边位变为 `1`。

例如十进制的正负 1，用 8 位二进制的原码表示如下：
- `[+1]` = 原: `[ 0000 0001 ]`
- `[-1]` = 原: `[ 1000 0001 ]`

### 2) 反码
- **正数**：和原码相同。
- **负数**：在其原码的基础上，**符号位不变，其余各位取反**。

示例：
- `[+1]` = 原: `[ 0000 0001 ]` = 反: `[ 0000 0001 ]`
- `[-1]` = 原: `[ 1000 0001 ]` = 反: `[ 1111 1110 ]`

### 3) 补码
- **正数**：补码是其原码本身。
- **负数**：补码是在其原码的基础上，**符号位不变，其余各位取反后加 1**（即在反码的基础上加 1）。

示例：
- `[+1]` = 原: `[ 0000 0001 ]` = 反: `[ 0000 0001 ]` = 补: `[ 0000 0001 ]`
- `[-1]` = 原: `[ 1000 0001 ]` = 反: `[ 1111 1110 ]` = 补: `[ 1111 1111 ]`

## 三、 数据在计算机中的存储形式

计算机实际上只存储**补码**，所以说原码转换为补码的过程，也可以理解为数据存储到计算机内存中的过程。

在原、反、补码中，正数的表示是一模一样的，而负数的表示是不相同的。因此对于负数的补码来说，我们不能直接用进制转换将其转换为十进制数值，因为这样是得不到计算机真正存储的十进制数的。**应该将其转换为原码后，再将转换得到的原码进行进制转换为十进制数**（机器数包含符号位）。

## 四、 为什么会使用原码、反码、补码

对于人脑来说，知道机器数的第一位是符号位是一件很轻松的事情，但对于计算机基础电路设计来说判别第一位是符号位是非常难和复杂的事情。

为了让计算机底层设计更加简单，于是设计将符号位参与运算，并且**只保留加法**的方法，通过**加上一个负数的方式来实现减法**。这样让计算机运算更加简单，并且也让符号位参与到运算中去。

## 五、 使用原码、反码与补码进行运算

### 1) 使用原码运算
计算十进制表达式：`1 - 1 = 0`
```text
1 - 1 = 1 + (-1)
      = 原:[ 0000 0001 ] + 原:[ 1000 0001 ]
      = 原:[ 1000 0010 ] = -2
```
**结论**：如果用原码表示，让符号位也参与计算，对于减法来说，结果是不正确的。这也是计算机内部在存储数据时不使用原码的原因。为了解决这一问题，出现了反码。

### 2) 使用反码运算
计算十进制表达式：`1 - 1 = 0`
```text
1 - 1 = 1 + (-1)
      = 原:[ 0000 0001 ] + 原:[ 1000 0001 ]
      = 反:[ 0000 0001 ] + 反:[ 1111 1110 ]
      = 反:[ 1111 1111 ] = 原:[ 1000 0000 ] = -0
```
**结论**：通过计算我们发现用反码计算减法，结果的真值部分是正确的。唯一的问题出现在 "0" 这个特殊的数值上。虽然人们理解上 `+0` 和 `-0` 是一样的，但是 0 带符号是没有任何意义的，而且会有 `[0000 0000]原` 和 `[1000 0000]原` 两个编码表示 0。为了解决这一问题，出现了补码。

### 3) 使用补码运算
计算十进制表达式：`1 - 1 = 0`
```text
1 - 1 = 1 + (-1)
      = 原:[ 0000 0001 ] + 原:[ 1000 0001 ]
      = 补:[ 0000 0001 ] + 补:[ 1111 1111 ]
      = 补:[ 0000 0000 ] = 原:[ 0000 0000 ] = 0
```
**结论**：这样 0 用 `[0000 0000]` 表示，而以前出现问题的 `-0` 则不存在了。而且人们还发现可以用 `[1000 0000]` 表示 `-128`。

**-128 的推算过程如下**：
```text
(-1) + (-127) = -128
              = 原:[ 1000 0001 ] + 原:[ 1111 1111 ]
              = 补:[ 1111 1111 ] + 补:[ 1000 0001 ]
              = 补:[ 1000 0000 ]
```
> **注意**：因为实际上是使用以前的 `-0` 的补码来表示 `-128`，所以 `-128` 并没有原码和反码表示。只要补码是 `[1000 0000]`，其十进制数值就为 `-128`。

### 4) 小结
因为补码能多存储一个 `-128`，而且在计算机底层中存储的是补码，所以在计算机中一个 8 位的二进制数的存储范围是用补码表示的 `[-128, 127]`，而不是用原码或反码表示的 `[-127, 127]`。这也可以解释为什么计算机中一个字节的取值范围是 `[-128, 127]`。

## 六、 总结（牢记）

- **二进制的最高位是符号位**：`0` 表示正数，`1` 表示负数。
- **正数三码合一**：正数的原码、反码、补码都一样。
- **负数反码**：它的原码符号位不变，其它位取反。
- **负数补码**：它的反码 + 1；反之，负数反码 = 负数补码 - 1。
- **0 的表示**：`0` 的反码、补码都是 `0`。
- **运算方式**：在计算机运算的时候都是以 **“补码”** 的方式来运算的。
- **查看结果**：当我们看运算结果的时候，要看它的**原码**（重点）。
