---
schema_version: "library-reader-v1"
source_type: "original_book"
title: "编码 隐匿在计算机软硬件背后的语言"
author: null
edition: null
publisher: null
year: null
isbn: "9787121181184"
library_document_id: "38270854-596f-4a26-8183-db8c968c6020"
source_sha256: "249786274d83909f0bddc34d77f81ed8f0b14d1eac8843c772a37a3ecbfe2cfc"
pdf_physical_page_count: 438
pdf_physical_page_index_base: 0
range_start: 200
range_count: 10
html_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020?start=200&limit=10"
markdown_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=200&limit=10"
previous_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=190&limit=10"
next_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=210&limit=10"
---

# 编码 隐匿在计算机软硬件背后的语言

> 页码规则：PDF_PHYSICAL_PAGE_INDEX 是从 0 开始的权威文件物理页索引；PRINTED_PAGE 是页面中识别出的印刷页码，可能为空，二者不得混用。

## PDF_PHYSICAL_PAGE_INDEX=200 | PRINTED_PAGE=184

时钟信号的每一个正跳变发生时，一些Q输出可能会改变，而另外一些可能不变，但总体上来说它们所表示的二进制编码递增了1。

本章前面提到过可以找到某种方法来确定振荡器频率，现在已经找到这种方法了。如果把一个振荡器连接到8位计数器的时钟输入端上，那么这个计数器会显示出振荡器经过的循环次数。当计数器总数达到11111111（十进制的255)，它又返回为00000000。使用计数器确定振荡器频率的最简单的方法就是把计数器的8个输出端分别接到8只灯泡上。当所有的输出都是0时（即所有灯泡都是熄灭的)，启动一个秒表计时；当所有灯泡都点亮时，停止秒表计时。这就是振荡器循环256次所需要的时间。假设这个时间为10s，则振荡器的频率是 $2 5 6 \div 1 0$ ，即 $2 5 . 6 \mathrm { H z } _ { \mathrm { \Omega } }$

随着触发器功能的增加，它的结构也变得更加复杂，下面给出了一个带预置和清零功能的边沿型D触发器。

通常情况下，预置和清零信号输入会覆盖时钟和数据端输入，且两个输入都为0，当预置信号为1时，Q变为1， $\overline { { \mathsf { Q } } }$ 变为 $\scriptstyle 0 _ { \circ }$ 当清零信号为1，Q变为0， $\overline { { \mathbf { Q } } }$ 变为1（同 R-S触发器中的S和R输入端一样，预置和清零信号不能同时为1)。除此之外，该触发器工作原理是和普通边沿D触发器是一样的。

编码隐匿在计算机软硬件背后的语言

184

## PDF_PHYSICAL_PAGE_INDEX=201 | PRINTED_PAGE=185

电路图符号可以简单地用下图来代替。

现在，我们已经懂得如何使用继电器来做加法、减法和计数了，这是一件很有成就感的事情，因为我们使用的硬件是100多年前就存在的东西。我们还有更多未知领域要去探索，请稍事休息，停止思考那些构造方面的问题，回过头再来看看数字方面的问题吧。

14 反馈与触发器

185

## PDF_PHYSICAL_PAGE_INDEX=202 | PRINTED_PAGE=NOT_DETECTED

字节与十六进制

通过对上一章中两类改进加法机的剖析，我们学习了数据路径(DataPath)这个概念。纵观整个电路的脉络，每8个比特流为一组，如潺潺溪流般在器件与器件之间流动。其实，这8位比特流就是加法器、锁存器以及数据选择器的输入形式，同时它也是这些器件单元的输出形式。这8位的比特流可以用开关的不同状态组合所定义，而且可以用灯泡的亮灭来显示。这样一来，这些电路中数据路径的位宽（bitswide）就是8。为什么我们要把它定义为“8”位呢？为什么没有定义为6位、7位、9位或10位呢？

要想用偷懒的方式回答上面这个问题也很简单，答案就是这些加法机都是对第12章中原始加法机的改进，而它所“流传”下来的位宽恰好就是8位。但是追根究底，原始加法机偏偏是8位其实没有什么特别的原因。只不过在每次使用8位位宽时，一切工作都显得非常方便一种优雅的比特化（biteful）的比特流。或许大家已经感觉到我在极力隐瞒一些东西，无法否认，其实我心中一直都很清楚（或许你也知道）：8比特代表一个字节（byte）。

字节这个词最早起源于1956年前后，由IBM公司提出。最早的拼写方式是bite，但为了避免与bit混淆用y代替了i。曾几何时，字节仅表示某一数据路径上的位数，直到20世纪60年代中叶，在IBM的360系统的发展下（一种大规模复杂的商用计算机)，字节这个词逐渐开始用来表示一组8比特数据。

## PDF_PHYSICAL_PAGE_INDEX=203 | PRINTED_PAGE=187

由于有8位，一个字节的取值范围为00000000到11111111。相应地，它还可以表示成为0\~255之间的正整数，如果将一个数的补码作为其相对应的负数，那么一个字节可以表示在-128\~127范围内的正、负整数。一个给定的字节可以代表 $2 ^ { 8 }$ ，即256种不同事物中的一个。

让我们再来看看“8”这个数字，当8作为比特流的一种尺度，它的确表现出了非常完美的特质。字节在很多方面都比单独的比特更胜一筹。IBM采用字节有一个很重要的原因，就是这样一来数字就可以按照BCD形式（第23章中将会讲述）方便地保存。在本章随后的讲述中，我们会发现凑巧的是：全世界大部分书面语言（除了中文、日文以及韩文中使用的象形文字体系)的基本字符数都少于256，所以字节是一种理想的保存文本的手段。字节同样适合表示黑白图像中的灰度值，这是由于肉眼能区分的灰度约为256种。当一个字节无法表示所有信息（如刚提到的中文、日文以及韩文中使用的象形文字体系等)，我们只需采用两个字节——就可以表示 $2 ^ { 1 6 }$ 也就是65536个不同的物体—这也是一种很好的解决方案。

字节的一半——即4比特———我们称之为半字节（nibble，也可拼写成nybble)，在计算机这个领域，它并不像字节那样经常使用。

由于字节在计算机内部出现的频率较高，如果可以使用一种简洁的方式将它的内在含义准确表达出来，将会为我们带来很多方便。假如一个8位二进制数表示为10110110，这种表达方式自然而又直观，但它还不够简洁。

我们完全可以采用十进制表示法来表示字节，但从二进制转换到十进制需要进行一系列计算一—计算方法并不复杂，但是比较麻烦。我曾在第8章曾介绍过一种直观的计算方法。因为每一位二进制数对应着2的不同幂，因此我们可以把二进制数写到第一行，在底下写出每一位二进制数对应的2的乘方数，然后采用“列相乘、行相加”就可以得到对应的十进制数。下图表示了10110110的转换过程。

把十进制数转换为二进制数就需要一点点技巧了。我们可以用这个十进制数不断除以递减排列的2的幂，每除一次得到的商就是所要求的二进制数中的一位，而余数成为下一次运算的除数，它的作用是除以下一个较小一点的2的幂。下图表示了十进制数182转换成二进制形式的过程。

15 字节与十六进制

187

## PDF_PHYSICAL_PAGE_INDEX=204 | PRINTED_PAGE=188

第8章对这种计算方法有更详细的描述。不过，在十进制数和二进制数之间进行转换还需用到一些工具，例如要借助笔和纸进行一系列演算。

第8章中我们还学到了八进制数，也称为八进制数字系统。这种系统仅使用到了数字0、1、2、3、4、5、6还有7。八进制数和二进制数之间的转换简洁方便，只要记住0\~7这8个数字所对应的3位二进制数即可。下面这张表就表示了这种对应关系。

假如要把一个二进制数（如10110110）转化为8进制，可以从最右端的数字开始。每3比特看做一组，这样每组便对应着一个八进制数：

$$
\frac { 1 9 } { 2 } \% \frac { 1 1 9 } { 6 }
$$

10110110这个字节很容易就表示为八进制数266。这种方法简洁明了，八进制用来表示字节不失为一个好方法。但还是有那么一点美中不足。

字节可以表示的二进制数的范围为00000000\~11111111，如果采用八进制表示，那么相对应的范围也随之变成了000\~377。仔细分析下先前的例子，我们从右到左把3位二进制数对应于中间以及最靠右的八进制数，而最靠左的八进制数却是由2位二进制数对

编码隐匿在计算机软硬件背后的语言

188

## PDF_PHYSICAL_PAGE_INDEX=205 | PRINTED_PAGE=189

应的。如果我们将16位二进制数直接表示为八进制会得到如下结果：

$$
\underbrace { { \downarrow } { \theta } 1 } _ { 3 } \underbrace { { \theta } { \theta } 1 } _ { 1 } \underbrace { 1 1 } _ { 7 } \underbrace { { \theta } { \theta } 0 } _ { 0 } \underbrace { 1 { \theta } 1 } _ { 5 }
$$

如果我们把这个16位二进制数平分为两个字节并将其分别表示为八进制数会得到如下所示的不同结果：

$$
\underbrace { 1 } _ { 2 } \underbrace { 1 } _ { 6 } \underbrace { 1 } _ { 3 } \qquad \underbrace { 1 } _ { 3 } \underbrace { 1 } _ { 0 } \underbrace { 1 } _ { 5 }
$$

为了使多字节值能和分开表示的单字节值取得一致，我们需要一种可以等分单个字节的系统，按照这种思想，我们可以把每个字节等分成4组，每组2比特（基于4的计数系统）；还可以等分为2组，每组4比特（基于16的计数系统）。

基于16的计数系统(Base16)，对于我们而言是一种全新的系统。基于16的计数系统也被称为十六进制（hexadecimal)，这个单词很容易让我们产生混淆。这是因为很多以hexa-为前缀的单词（比如 Hexagon，Hexapod 以及 Hexameter）都会与 6或多或少有些联系。而hexadecimal这个词却偏偏代表了16（Sixteen）这个含义。虽然在《微软出版物风格与技术手册》(The Microsoft Manual of Style for Technical Publications)中明确说明“请勿将十六进制缩写为hex”，但包括我在内的绝大多数人还总是在不经意间使用这种缩写。

十六进制还有其他一些特别之处。比如在十进制中，我们通常可以用下面这种方式计数：

$$
0 \ 1 \ 2 \ 3 \ 4 \ 5 \ 6 \ 7 \ 8 \ 9 \ 1 0 \ 1 1 \ 1 2 . . .
$$

我们仔细回忆一下在八进制中，8和9这两个数字是不需要的，就像下面这样：

$$
0 ~ 1 ~ 2 ~ 3 ~ 4 ~ 5 ~ 6 ~ 7 ~ 1 0 ~ 1 1 ~ 1 2 . . .
$$

同理，四进制计数中我们也不需要4、5、6、7这些数字，就像下面这样：

$$
\smash { 0 \uparrow 2 3 1 0 1 1 1 2 \ldots }
$$

在二进制中，这一切变得更加简单，我们所需要的只有0和1：

$$
0 \mid 1 0 1 1 1 0 0 \ldots
$$

十六进制计数和上面所讲的这一系列情况是完全不同的，它需要比十进制更多的数

15 字节与十六进制

189

## PDF_PHYSICAL_PAGE_INDEX=206 | PRINTED_PAGE=190

字来计数。十六进制的计数过程将会是下面这种形式：

0 1 2 3 4 5 6 7 8 9??????10 11 12...

上图中10（准确地来说是一个1紧挨着一个0）这个数字代表的准确含义实际上应该是十进制中的16。图中的问号表明我们还需要6个符号来完整表示十六进制系统。这些符号究竟是什么？它们出于何处？仔细想想，原有的计数系统中的符号都有自己的唯一身份，所以应该而且必须引入新的符号。寻找6个新符号对我们来说是小菜一碟，例如下面这几个符号：

不像我们所使用的大多数数字符号，上面列出的每个符号都很容易识记，而且其背后都隐含着实际数字意义，这种形象的符号使得它们便于记忆。比如这个10加仑的牛仔帽、一个橄榄球（11个人组成一支橄榄球队）、一打（12个）面包圈、一只黑猫（使人们想起不吉利的13）、一轮满月（一般出现在后弦月14天之后的夜晚)，一把匕首让人们联想到凯撒大帝（JuliusCaesar）在三月的月中（第15日）被刺杀。

两个十六进制数可以完整地代表一个字节。这也意味着一个十六进制数恰好由4位二进制数组成，即半字节。下面这张表描述了如何在二进制、十六进制、十进制数之间进行转换。

编码—隐匿在计算机软硬件背后的语言

190

## PDF_PHYSICAL_PAGE_INDEX=207 | PRINTED_PAGE=191

让我们再来看看如何把字节10110110转换成十六进制。

$$
\frac { 1 \theta 1 1 } { 5 } \frac { \theta 1 1 \div \theta } { 6 }
$$

无论是多字节还是单字节，我们都可以进行转换。

$$
\begin{array} { r l } { \underbrace { 1 8 1 1 } _ { \mathrm { ~ \tiny ~  ~ } } \underbrace { 0 1 1 8 } _ { 6 } \qquad } & { { } \underbrace { 1 1 0 8 } _ { \mathrm { ~ \tiny ~  ~ } } \underbrace { 0 1 8 1 } _ { 5 } } \end{array}
$$

一个字节能且只能由一对十六进制数来表示。

只可惜（或许你也松了口气）我们不打算用橄榄球或一打面包圈来表示十六进制数。虽然这种方案完全可行，但它不是很方便也不够正规，有时还会让人感到迷惑。事实上十六进制中缺少的6个符号由6个拉丁字母来表示，就像下面这样：

0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12...

下面这张表描述了正规的二进制、十六进制、十进制转换的过程。

15 字节与十六进制

191

## PDF_PHYSICAL_PAGE_INDEX=208 | PRINTED_PAGE=192

这样一来字节10110110就可以表示为十六进制的B6，而不用画一个橄榄球那么麻烦。我们再回忆回忆前面的章节，下面举一个完整的进制转换的例子（用下标代表进制），比如：

$$
1 0 1 1 0 1 1 0 _ { \mathrm { T w o } } \mathrm { ~ ( ~ } 1 0 1 1 0 1 1 0 _ { 2 } \mathrm { ~ ) ~ }
$$

它的四进制形式表示为：

$$
2 3 1 2 _ { \mathrm { F O U R } } \ ( \ 2 3 1 2 _ { 4 } )
$$

它的八进制形式表示为：

$$
2 6 6 _ { \tt E I T H T } \ : \left( \ : 2 6 6 _ { \tt S } \right)
$$

它的十进制形式表示为：

$$
1 8 2 _ { \tt T E N } \ : \left( \ : 1 8 2 _ { 1 0 } \right)
$$

与此类似，它的十六进制形式可以表示为：

$$
\mathbf { B } 6 _ { \mathrm { S I X T E E N } } \ ( \ \mathbf { B } 6 _ { 1 6 } \ )
$$

但是这种写法有点累赘，幸好还有几种表达十六进制数通用方法。你还可以用如下方法表示：

$$
\mathsf { B 6 } _ { \mathtt { H E X } }
$$

在本书中将使用一种更加简洁实用的方法，那就是用一个小写的h紧跟在数字后边表示这个数是以十六进制表示的，就像这样：

B6h

通过分析可以得到十六进制数的每一位代表16的不同整数幂的倍数，如下图所示。

编码—隐匿在计算机软硬件背后的语言

192

## PDF_PHYSICAL_PAGE_INDEX=209 | PRINTED_PAGE=193

十六进制数9A48Ch可以表示为如下形式：

$$
{ \begin{array} { r l } { 9 . \mathbf { A } 4 8 \mathbf { C } \mathbf { h } = 9 \times 1 0 0 0 \mathbf { h } + } & { } \\ { \mathbf { A } \times 1 0 0 0 \mathbf { h } + } & { } \\ { 4 \times 1 0 0 \mathbf { h } + } & { } \\ { 8 \times 1 0 \mathbf { h } + } & { } \\ { \mathbf { C } \times 1 \mathbf { h } } & { } \end{array} }
$$

这个数用16的乘方表示可以写为：

$$
\begin{array} { r } { 9 \mathrm { A } 4 8 \mathrm { C h } = 9 \times 1 6 ^ { 4 } + } \\ { \mathrm { A } \times 1 6 ^ { 3 } + } \\ { 4 \times 1 6 ^ { 2 } + } \\ { 8 \times 1 6 ^ { \dagger } + } \\ { \mathrm { C } \times 1 6 ^ { 0 } } \end{array}
$$

也可以将16的幂进一步展开，写为如下形式：

$$
{ \begin{array} { r c l } { 9 { \mathrm { A } } 4 8 { \mathrm { C h } } = 9 \times 6 5 , 5 3 6 } \\ & { } \\ { \qquad \mathrm { A } \times 4 0 9 6 \ + } \\ & { } \\ { 4 \times 2 5 6 \ + } \\ & { } \\ { 8 \times 1 6 \ + } \\ & { } \\ { \qquad \subset \times 1 } \end{array} }
$$

我们可以仔细想想，如果只把一个数字（比如9，A，4，8和C中的任何一个）单独列举出来，而且不使用任何下标来指明其进制数，这种做法并不会产生二义性。无论在十进制还是十六进制下，一个单独的9仅代表9；而A的出现也说明了它本身是一个十六进制数—等价于十进制中的10。

我们可以拿起笔和纸进行演算了，把十六进制数转换成十进制数：

$$
\begin{array} { c } { 9 \mathrm { A } 4 8 \mathrm { C h } = 9 \times 6 5 , 5 3 6 \ + } \\ { 1 0 \times 4 0 9 6 \ + } \\ { 4 \times 2 5 6 \ + } \\ { 8 \times 1 6 \ + } \\ { 1 2 \times 1 } \end{array}
$$

运算的最后结果是631,948。这就是一个十六进制数转换成为十进制数的完整过程。

下面给出了一种模板，它可以帮助我们把4位十六进制转换成为十进制。

15 字节与十六进制

193

下一段：[继续读取](https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=210&limit=10)
