Fix the figures.

Polish the chapter of data structure.
This commit is contained in:
krahets
2023-08-13 19:32:11 +08:00
parent ec82be7dc2
commit 0c18198c01
14 changed files with 79 additions and 73 deletions

View File

@@ -2,11 +2,13 @@
!!! note
在本书中,标题带有的 * 符号的是选读章节。如果你时间有限或感到理解困难,建议先跳过,等学完必读章节后再单独攻克。
在本书中,标题带有的 * 符号的是选读章节。如果你时间有限或感到理解困难,可以先跳过,等学完必读章节后再单独攻克。
## 原码、反码和补码
从上一节的表格中我们发现,所有整数类型能够表示的负数都比正数多一个。例如,`byte` 的取值范围是 $[-128, 127]$ 。这个现象比较反直觉,它的内在原因涉及到原码、反码、补码的相关知识。在展开分析之前,我们首先给出三者的定义:
从上一节的表格中我们发现,所有整数类型能够表示的负数都比正数多一个。例如,`byte` 的取值范围是 $[-128, 127]$ 。这个现象比较反直觉,它的内在原因涉及到原码、反码、补码的相关知识。
在展开分析之前,我们首先给出三者的定义:
- **原码**:我们将数字的二进制表示的最高位视为符号位,其中 $0$ 表示正数,$1$ 表示负数,其余位表示数字的值。
- **反码**:正数的反码与其原码相同,负数的反码是对其原码除符号位外的所有位取反。
@@ -14,7 +16,7 @@
![原码、反码与补码之间的相互转换](number_encoding.assets/1s_2s_complement.png)
显然「原码」最为直观,**然而数字是以「补码」的形式存储在计算机中的**。这是因为原码存在一些局限性。
显然「原码」最为直观。但实际上**数字是以「补码」的形式存储在计算机中的**。这是因为原码存在一些局限性。
一方面,**负数的原码不能直接用于运算**。例如,我们在原码下计算 $1 + (-2)$ ,得到的结果是 $-3$ ,这显然是不对的。
@@ -27,20 +29,20 @@ $$
\end{aligned}
$$
为了解决此问题,计算机引入了「反码」。例如,我们先将原码转换为反码,并在反码下计算 $1 + (-2)$ 将结果从反码转化回原码,则可得到正确结果 $-1$ 。
为了解决此问题,计算机引入了「反码」。如果我们先将原码转换为反码,并在反码下计算 $1 + (-2)$ 最后将结果从反码转化回原码,则可得到正确结果 $-1$ 。
$$
\begin{aligned}
& 1 + (-2) \newline
& = 0000 \space 0001 \space \text{(原码)} + 1000 \space 0010 \space \text{(原码)} \newline
& \rightarrow 0000 \space 0001 \space \text{(原码)} + 1000 \space 0010 \space \text{(原码)} \newline
& = 0000 \space 0001 \space \text{(反码)} + 1111 \space 1101 \space \text{(反码)} \newline
& = 1111 \space 1110 \space \text{(反码)} \newline
& = 1000 \space 0001 \space \text{(原码)} \newline
& = -1
& \rightarrow -1
\end{aligned}
$$
另一方面,**数字零的原码有 $+0$ 和 $-0$ 两种表示方式**。这意味着数字零对应着两个不同的二进制编码,而这可能会带来歧义问题。例如,在条件判断中,如果没有区分正零和负零,可能会导致错误的判断结果如果我们想要处理正零和负零歧义,则需要引入额外的判断操作,其可能会降低计算机的运算效率。
另一方面,**数字零的原码有 $+0$ 和 $-0$ 两种表示方式**。这意味着数字零对应着两个不同的二进制编码,可能会带来歧义。比如在条件判断中,如果没有区分正零和负零,可能会导致判断结果出错。而如果我们想要处理正零和负零歧义,则需要引入额外的判断操作,其可能会降低计算机的运算效率。
$$
\begin{aligned}
@@ -49,7 +51,7 @@ $$
\end{aligned}
$$
与原码一样,反码也存在正负零歧义问题。为此,计算机进一步引入了「补码」。那么,补码有什么作用呢?我们先来分析一下负零的补码的计算过程:
与原码一样,反码也存在正负零歧义问题,因此计算机进一步引入了「补码」。我们先来观察一下负零的原码、反码、补码的转换过程:
$$
\begin{aligned}
@@ -59,30 +61,30 @@ $$
\end{aligned}
$$
在负零的反码基础上加 $1$ 会产生进位,而由于 byte 的长度只有 8 位,因此溢出到第 9 位的 $1$ 会被舍弃。**从而得到负零的补码为 $0000 \space 0000$ ,与正零的补码相同**。这意味着在补码表示中只存在一个零,从而解决了正负零歧义问题
在负零的反码基础上加 $1$ 会产生进位,`byte` 类型的长度只有 8 位,因此溢出到第 9 位的 $1$ 会被舍弃。也就是说,**负零的补码为 $0000 \space 0000$ ,与正零的补码相同**。这意味着在补码表示中只存在一个零,正负零歧义从而得到解决
还剩余最后一个疑惑byte 的取值范围是 $[-128, 127]$ ,多出来的一个负数 $-128$ 是如何得到的呢?我们注意到,区间 $[-127, +127]$ 内的所有整数都有对应的原码、反码和补码,并且原码和补码之间是可以互相转换的。
还剩余最后一个疑惑:`byte` 类型的取值范围是 $[-128, 127]$ ,多出来的一个负数 $-128$ 是如何得到的呢?我们注意到,区间 $[-127, +127]$ 内的所有整数都有对应的原码、反码和补码,并且原码和补码之间是可以互相转换的。
然而,**补码 $1000 \space 0000$ 是一个例外,它并没有对应的原码**。根据转换方法,我们得到该补码的原码为 $0000 \space 0000$ 。这显然是矛盾的,因为该原码表示数字 $0$ ,它的补码应该是自身。计算机规定这个特殊的补码 $1000 \space 0000$ 代表 $-128$ 。实际上,$(-1) + (-127)$ 在补码下的计算结果就是 $-128$ 。
$$
\begin{aligned}
& (-127) + (-1) \newline
& = 1111 \space 1111 \space \text{(原码)} + 1000 \space 0001 \space \text{(原码)} \newline
& \rightarrow 1111 \space 1111 \space \text{(原码)} + 1000 \space 0001 \space \text{(原码)} \newline
& = 1000 \space 0000 \space \text{(反码)} + 1111 \space 1110 \space \text{(反码)} \newline
& = 1000 \space 0001 \space \text{(补码)} + 1111 \space 1111 \space \text{(补码)} \newline
& = 1000 \space 0000 \space \text{(补码)} \newline
& = -128
& \rightarrow -128
\end{aligned}
$$
你可能已经发现,上述的所有计算都是加法运算。这暗示着一个重要事实:**计算机内部的硬件电路主要是基于加法运算设计的**。这是因为加法运算相对于其他运算(比如乘法、除法和减法)来说,硬件实现起来更简单,更容易进行并行化处理,从而提高运算速度。
你可能已经发现,上述的所有计算都是加法运算。这暗示着一个重要事实:**计算机内部的硬件电路主要是基于加法运算设计的**。这是因为加法运算相对于其他运算(比如乘法、除法和减法)来说,硬件实现起来更简单,更容易进行并行化处理,运算速度更快
然而,这并不意味着计算机只能做加法。**通过将加法与一些基本逻辑运算结合,计算机能够实现各种其他的数学运算**。例如,计算减法 $a - b$ 可以转换为计算加法 $a + (-b)$ ;计算乘法和除法可以转换为计算多次加法或减法。
请注意,这并不意味着计算机只能做加法。**通过将加法与一些基本逻辑运算结合,计算机能够实现各种其他的数学运算**。例如,计算减法 $a - b$ 可以转换为计算加法 $a + (-b)$ ;计算乘法和除法可以转换为计算多次加法或减法。
现在我们可以总结出计算机使用补码的原因:基于补码表示,计算机可以用同样的电路和操作来处理正数和负数的加法,不需要设计特殊的硬件电路来处理减法,并且无需特别处理正负零的歧义问题。这大大简化了硬件设计,提高了运算效率。
现在我们可以总结出计算机使用补码的原因:基于补码表示,计算机可以用同样的电路和操作来处理正数和负数的加法,不需要设计特殊的硬件电路来处理减法,并且无需特别处理正负零的歧义问题。这大大简化了硬件设计,提高了运算效率。
补码的设计非常精妙,由于篇幅关系我们先介绍到这里建议有兴趣的读者进一步深度了解。
补码的设计非常精妙,篇幅关系我们先介绍到这里建议有兴趣的读者进一步深度了解。
## 浮点数编码
@@ -123,7 +125,7 @@ $$
![IEEE 754 标准下的 float 表示方式](number_encoding.assets/ieee_754_float.png)
以上图为例,$\mathrm{S} = 0$ $\mathrm{E} = 124$ $\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$ 易得
给定一个示例数据 $\mathrm{S} = 0$ $\mathrm{E} = 124$ $\mathrm{N} = 2^{-2} + 2^{-3} = 0.375$ 则有:
$$
\text { val } = (-1)^0 \times 2^{124 - 127} \times (1 + 0.375) = 0.171875