mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-01 01:54:24 +00:00
72 lines
6.8 KiB
Markdown
72 lines
6.8 KiB
Markdown
# Кодирование чисел *
|
||
|
||
!!! tip
|
||
|
||
В этой книге разделы, отмеченные символом *, являются необязательными для чтения. Если у вас ограничено время или возникают трудности с пониманием, вы можете пропустить их и вернуться к ним после изучения обязательных разделов.
|
||
|
||
## Прямой, обратный и дополнительный коды
|
||
|
||
В таблице из предыдущего раздела можно заметить, что во всех целочисленных типах отрицательных чисел на одно больше, чем положительных. Например, диапазон значений `byte` составляет $[-128, 127]$. Этот факт кажется не совсем интуитивным, и его внутренняя причина связана с концепциями прямого, обратного и дополнительного кодов.
|
||
|
||
Прежде всего необходимо отметить, что **числа хранятся в компьютере в виде дополнительного кода**. Прежде чем проанализировать причины этого, сначала дадим определения всем трем кодам.
|
||
|
||
- **Прямой код**: старший бит двоичного представления числа рассматривается как знак, где $0$ обозначает положительное число, $1$ -- отрицательное, остальные биты представляют значение числа.
|
||
- **Обратный код**: обратный код положительного числа совпадает с его прямым кодом, обратный код отрицательного числа получается инверсией всех битов прямого кода, кроме знакового.
|
||
- **Дополнительный код**: дополнительный код положительного числа совпадает с его прямым кодом, дополнительный код отрицательного числа получается добавлением $1$ к его обратному коду.
|
||
|
||
На рис. 3.4 изображены методы преобразования прямого, обратного и дополнительного кодов между собой.
|
||
|
||

|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:<u>原码(sign-magnitude)</u>虽然最直观,但存在一些局限性。一方面,**负数的原码不能直接用于运算**。例如在原码下计算 $1 + (-2)$ ,得到的结果是 $-3$ ,这显然是不对的... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:$$\begin{aligned}& 1 + (-2) \newline& \rightarrow 0000 \; 0001 + 1000 \; 0010 \newline& = 1000 \; 0011 \newline& \rightarrow -3\end{aligned}$$... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:为了解决此问题,计算机引入了<u>反码(1's complement)</u>。如果我们先将原码转换为反码,并在反码下计算 $1 + (-2)$ ,最后将结果从反码转换回原码,则可得到正确结果 $-1$... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:$$\begin{aligned}& 1 + (-2) \newline& \rightarrow 0000 \; 0001 \; \text{(原码)} + 1000 \; 0010 \; \text{(原码)} \newline& = 0000 \; 0001 \; \text{(反码)} + 1111 \; 1101 \; \text{(反码)}... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:另一方面,**数字零的原码有 $+0$ 和 $-0$ 两种表示方式**。这意味着数字零对应两个不同的二进制编码,这可能会带来歧义... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:$$\begin{aligned}+0 & \rightarrow 0000 \; 0000 \newline-0 & \rightarrow 1000 \; 0000\end{aligned}$$... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:与原码一样,反码也存在正负零歧义问题,因此计算机进一步引入了<u>补码(2's complement)</u>。我们先来观察一下负零的原码、反码、补码的转换过程... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:$$\begin{aligned}-0 \rightarrow \; & 1000 \; 0000 \; \text{(原码)} \newline= \; & 1111 \; 1111 \; \text{(反码)} \newline= 1 \; & 0000 \; 0000 \; \text{(补码)} \newline\end{aligned}$$... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:在负零的反码基础上加 $1$ 会产生进位,但 `byte` 类型的长度只有 8 位,因此溢出到第 9 位的 $1$ 会被舍弃。也就是说,**负零的补码为 $0000 \; 0000$ ,与正零的补码相同**... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:还剩最后一个疑惑:`byte` 类型的取值范围是 $[-128, 127]$ ,多出来的一个负数 $-128$ 是如何得到的呢?我们注意到,区间 $[-127, +127]$ 内的所有整数都有对应的原码、反码和补码... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:然而,**补码 $1000 \; 0000$ 是一个例外,它并没有对应的原码**。根据转换方法,我们得到该补码的原码为 $0000 \; 0000$... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:$$\begin{aligned}& (-127) + (-1) \newline& \rightarrow 1111 \; 1111 \; \text{(原码)} + 1000 \; 0001 \; \text{(原码)}... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:你可能已经发现了,上述所有计算都是加法运算。这暗示着一个重要事实:**计算机内部的硬件电路主要是基于加法运算设计的**... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:请注意,这并不意味着计算机只能做加法。**通过将加法与一些基本逻辑运算结合,计算机能够实现各种其他的数学运算**... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:现在我们可以总结出计算机使用补码的原因:基于补码表示,计算机可以用同样的电路和操作来处理正数和负数的加法... -->
|
||
|
||
<!-- 🔴 俄文版缺失此段落 -->
|
||
<!-- 中文原文:补码的设计非常精妙,因篇幅关系我们就先介绍到这里,建议有兴趣的读者进一步深入了解。 -->
|
||
|
||
## Кодирование чисел с плавающей запятой
|
||
|
||
<!-- 🔴 俄文版缺失整个"浮点数编码"章节 -->
|
||
<!-- 中文原文:细心的你可能会发现:`int` 和 `float` 长度相同,都是 4 字节 ,但为什么 `float` 的取值范围远大于 `int`... --> |