---
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: 380
range_count: 10
html_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020?start=380&limit=10"
markdown_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=380&limit=10"
previous_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=370&limit=10"
next_url: "https://workshop.closeclaw.top/api/ai/read/38270854-596f-4a26-8183-db8c968c6020.md?start=390&limit=10"
---

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

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

## PDF_PHYSICAL_PAGE_INDEX=380 | PRINTED_PAGE=364

如果在一个操作系统上可以同时运行多个程序，则称此系统为多任务（multitasking）操作系统。显然，与CP/M和MS-DOS这样的单任务的系统相比，这种操作系统要复杂得多。正是由于支持多任务这种功能，文件系统变得很复杂，因为同一个文件可能被多个用户同时访问。程序的运行需要占用内存空间，多任务系统就要考虑内存的分配问题，也就是说需要进行内存管理（memorymanagement)。也许你会有疑问，多道程序并行运行需要占用大量内存，如果内存不够怎么办？为此，操作系统引入了虚拟内存（virtualmemory）技术。虚拟内存是指，在磁盘上划出部分空间用做保存临时文件，程序把暂时不需要用的内存块放到临时文件里，待需要时再把它调入内存。。

UNIX能够存在并发展到现在是无数人共同努力的结晶，如今 FSF（Free SoftwareFoundation，自由软件基金会）和GNU项目为推动UNIX的发展注入了新的活力，它们都是由理查德·斯塔门（Richard Stallman）创建的。GNU意味着：“GNU与 UNIX，既要划清界限又相辅相成”。GNU项目的宗旨是：创建一个与UNIX系统兼容，但不受私有权限制的操作系统和开发环境。在这个项目的推动下，涌现出了许多和UNIX兼容的实用程序和工具，其中最著名的要算Linux系统。Linux系统的内核(Core与Kernel 都可以表示内核的意思）和UNIX的是完全兼容的，它的大部分程序是由芬兰的李纳斯·托沃兹（LinusTorvalds）完成的。由于Linux系统的开源性，近年来已成为非常流行的操作系统。

从20世纪80年代中期开始，开发大型的、复杂度更高的系统成为了操作系统发展的一大趋势，例如苹果公司的Macintosh系统和微软的Windows系统，它们融合了图形和高级可视化视频显示技术，使应用程序变得更易于使用。关于图形和可视化方面的发展趋势，我们将在本书的最后一章进一步介绍。

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

364

## PDF_PHYSICAL_PAGE_INDEX=381 | PRINTED_PAGE=NOT_DETECTED

定点数和浮点数

数字就是数字，整数、分数以及百分数等各种类型的数字与我们形影不离，它几乎出现在我们生活的所有角落。例如你加班2.75小时，而公司按正常工作时间的1.5倍支付你工资，你用这些钱买了半盒鸡蛋并交了8.25%的销售税。就算你不是数字研究方面的专家，也可能对这种“数字生活”非常熟悉。我们还经常听到类似这样的统计信息“美国每个家庭的平均人口是2.6人”，但谁也不会为了满足这个数字表达的真实含义而把人拆解掉（这种事情想起来都觉得恐怖）。

在计算机存储器中，整数和分数之间的转换并不是这么随意。现在我们应该清楚，计算机中的一切数据都是以位的形式存储的，这就意味着所有的数都表示为二进制形式。但另一个不可否认的事实是，某些类型的数比其他类型更容易用位的形式来表示。

我们将从整数的二进制表示开始，这里的整数被数学家称做“自然数”(positive wholenumbers)，即计算机程序员口中的“正整数”（positive integers)，之后将介绍如何利用2的补数来表示“负整数”(negativeintegers)，该方法可以让正数和负数的相加变得非常简单。下面的表格列出了8位、16位、32位二进制数所能表示的正整数及其2的补数的范围。

## PDF_PHYSICAL_PAGE_INDEX=382 | PRINTED_PAGE=366

我们所要介绍的整数部分就是这些。除此之外，数学家还定义了用两个整数的比值表示的一类数，称做有理数（rational number）或分数（fraction）。例如，3/4是一个有理数，因为它是整数3和4的比。我们也可以把3/4表示成十进制小数的形式，即0.75。尽管我们可以把它写成十进制数的形式，但它实际上代表一个分数，即75/100。

如第7章所述，在十进制数字系统中，小数点左边的数的每一位都和10的正整数次幂相关，而其右边的数的每一位都和10的负整数次幂相关。我们在第7章用到了42705.684这个实例，现在，我们把它表示为以下等价形式：

$$
\begin{array} { l } { { 4 \times 1 0 0 0 0 + } } \\ { { 2 \times 1 0 0 0 + } } \\ { { } } \\ { { 7 \times 1 0 0 + } } \\ { { 0 \times 1 0 + } } \\ { { 5 \times 1 + } } \\ { { 6 + 1 0 + } } \\ { { 8 + 1 0 0 + } } \\ { { 4 + 1 0 0 0 } } \end{array}
$$

上面的除号表达了该位置的数与10的负整数次幂相关的情况，用下面的表达方式可以不用除号：

$$
\begin{array} { r l } & { 4 \times 1 0 0 0 0 + } \\ & { 2 \times 1 0 0 0 + } \\ & { 7 \times 1 0 0 + } \\ & { 0 \times 1 0 + } \\ & { 5 \times 1 + } \\ & { 6 \times 0 . 1 + } \\ & { 8 \times 0 . 0 1 + } \end{array}
$$

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

366

## PDF_PHYSICAL_PAGE_INDEX=383 | PRINTED_PAGE=367

$$
4 \times 0 . 0 0 1
$$

最后，我们将该数表示为10的幂的形式：

$$
4 \times 1 0 ^ { 4 } +
$$

$$
2 \times 1 0 ^ { 3 } +
$$

$$
7 \times 1 0 ^ { 2 } +
$$

$$
0 \times 1 0 ^ { 1 } +
$$

$$
5 \times 1 0 ^ { 0 } +
$$

$$
6 \times 1 0 ^ { - 1 } +
$$

$$
{ \bf 8 } \times 1 0 ^ { - 2 } +
$$

$$
4 \times 1 0 ^ { - 3 }
$$

有一些有理数很难表示成小数，最明显的一个例子是1/3，如果用3来除1，会得到这样的结果：

0.3333333333333333333333333333333333333333333333333333333333...

在小数点的后面将会有无数个3。通过在第一个3的上面加一条短横线，可以将这个无限循环的数简单表示为：

$$
\mathbf { 0 . \bar { 3 } }
$$

尽管如此，把1/3表示为小数还不是很方便，但它毕竟是一个有理数，因为在本质上它是两个整数的比。类似的，1/7可以表示为：

0.1428571428571428571428571428571428571428571428571428571...

或

0.142857

无理数（irrationalnumber）是一些更加奇特的数，如2的平方根等。它们不能表示为两个整数的比，这就意味着其小数部分是无穷的，而且毫无规律，没有循环，例如：

√2≈1.41421356237309504880168872420969807856967187537695...

下面这个数学方程式的正数解就是2的平方根：

23 定点数和浮点数

367

## PDF_PHYSICAL_PAGE_INDEX=384 | PRINTED_PAGE=368

$$
x ^ { 2 } - 2 = 0
$$

如果某个数不是任何以整数为系数的代数方程的解，那么这个数称做超越数(transcendental，所有的超越数都是无理数，但是反之不成立)。π就是一个典型的超越数，它是圆的周长与其直径的比值，可以近似的表示为：

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

e是另一个典型的超越数，它是数学表达式：

$$
( 1 + { \frac { 1 } { n } } ) ^ { n }
$$

当n趋向无穷大时的值，近似的值为：

2.71828182845904523536028747135266249775724709369996...

目前我们所讨论过的所有数—有理数和无理数—统称为实数（real numbers)。使用实数定义它们的目的是为了将其与虚数（imaginarynumbers）区别开来，虚数是负数的平方根。实数和虚数一起构成了复数（complexnumbers)。不管名称如何，它们都有重要的作用，例如，虚数确实存在于现实世界，它在解决电子学的某些高级问题中有重要应用。

我们习惯于把数字看做连续（continuous）的，任意给出两个有理数，都可以找出一个位于它们之间的数。实际上，只需要取这两个数的平均值即可。但是，数字计算机对连续数据却无能为力，因为二进制中的每一位非0即1，两者之间没有任何数。这一特点决定了数字计算机只能处理离散（discrete）数据。二进制数的位数直接决定了所能表示的离散数值的个数。例如，如果你选择的二进制位数是32，则所能表示的自然数的范围是0\~4,294,967,295。如果想要在计算机中存储4.5这个数，则需要选择新的方法并做一些其他方面的改进。

小数也可以表示为二进制数吗？当然可以，最简单的方法可能就是使用BCD码（二进制编码的十进制数)。如第19章所述，BCD码是将十进制数以二进制的形式进行编码，0\~9之间的每一个数都需要用4位来表示，如下表所示。

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

368

## PDF_PHYSICAL_PAGE_INDEX=385 | PRINTED_PAGE=369

BCD编码在程序处理用美元和美分表示的钱款、账户时特别有用。银行和保险公司是非常典型的两类整日与钱打交道的机构，这些机构所使用的计算机程序中，大多数小数所占用的存储空间仅仅相当于两个十进制数所占用的位数。

通常把两个BCD数字存放在一个字节，这种方式称为压缩 BCD（packed BCD)。由于2的补数不和BCD数一起使用，因此压缩BCD通常需要增加1位用来标识数的正负，该位被称做符号位（signbit)。用一整个字节保存某个特定的BCD数是很方便的，但要为这个短小的符号位牺牲4位或8位的存储空间。

让我们来看一个例子，假设计算机程序所要处理的钱款数目在+/-100万美元之间，这就意味着，需要表示的钱的数目的范围是-9,999,999.99\~99,999,999.99，因此保存在存储器中的每一笔钱的金额都需要5个字节。因此，-4,325,120.25可以表示为下面5个字节：

00010100 00110010 01010001 00100000 00100101

将每个字节转换成十六进制数，上面的数可以等价地表示成：

14h 32h 51h 20h 25h

注意，最左边的半个字节所构成的1用来指明该数是负数，这个1即符号位。如果这半个字节所构成的数是0，则说明该数是正数。组成该数的每一个数字都需要用4位来表示，从十六进制的表示形式中可以很直观地看到这一点。

如果要表示的数的范围扩大到-99,999,999.99\~99,999,999.99，则我们需要用6个字

23 定点数和浮点数

369

## PDF_PHYSICAL_PAGE_INDEX=386 | PRINTED_PAGE=370

节来实现，其中5个字节用来表示10个数字，另一个字节整个用来做符号位。

这种基于二进制的存储和标记方式也被称作定点格式(fixed-point format),所谓的“定点”是指小数点的位置总是在数的某个特定位置在本例中，它位于两位小数之前。值得注意的是，有关小数点位置的计数信息并没有与整个数字一起存储。所以，使用定点小数的程序必须知道小数点的位置。你可以设计有任意小数位的定点小数，并且可以在程序中混合使用它们，但程序中对这些数进行算术运算的部分都需要知道小数点的位置，这样才能正确地对其做各种运算处理。

如果可以确定程序用到的数字不会大到超过预定的存储空间，并且这些数的小数位不会很多，那么使用定点格式的小数将是一个很好的选择。在表示非常大或非常小的数时，使用定点格式数是绝对不合适的。假设需要保留一块内存空间用来存放以英尺为单位的距离数据，可能存在的问题是某些距离的长度可能超出范围。地球与太阳之间的距离是490,000,000,000英尺，而氢原子的半径只有0.000,000,000,26英尺，如果采用定点格式的存储方案，为了存储这些极大或极小的数需要12个字节。

科学家和工程师们喜欢使用一种称为“科学计数法”（scientificnotation）的方法来记录这类较大或较小的数，利用这种计数系统可以更好地在计算机中存储这些数。科学计数法把每个数表示成有效位与10的幂的乘积的形式，这样就可以避免写一长串的0，因此这种计数方式特别适合表示极大或极小的数。采用科学计数法，下面这个数：

490,000,000,000

可以记为：

$$
4 . 9 \times 1 0 ^ { 1 1 }
$$

而

0.00000000026

可以表示为：

$$
2 . 6 \times 1 0 ^ { - 1 0 }
$$

在上面的两个例子中，4.9和2.6被称做小数部分或者首数（characteristic)，有时候

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

370

## PDF_PHYSICAL_PAGE_INDEX=387 | PRINTED_PAGE=371

也被称作尾数（mantissa，这个词通常与对数运算一起使用）。在计算机术语中这一部分被称做有效数(significand)，因此为了保持一致，这里把科学计数法表示形式中的这一部分也称作有效数。

采用科学计数法表示的数可以分为两部分，其中指数（exponent）部分用来表示10的几次幂。在第一个例子中，指数是11，第二个例子中指数是一10。指数可以表明小数点相对于有效数移动的距离。

为了便于操作，一般规定有效数的取值范围是大于或等于1而小于10。尽管下面列出的各种写法表示的都是同一个数：

$$
4 . 9 \times 1 0 ^ { 1 1 } = 4 9 \times 1 0 ^ { 1 0 } = 4 9 0 \times 1 0 ^ { 9 } = 0 . 4 9 \times 1 0 ^ { 1 2 } = 0 . 0 4 9 \times 1 0 ^ { 1 3 }
$$

但是上面等式中的第一种写法是最恰当的。这种写法有时被称做科学计数法的规范化式(normalized)。

这里需要说明，指数的正负性只是表明了数的大小，它并不能指明数本身的正负性。下面列出两个负数的科学计数法表示形式：

$$
- 5 . 8 1 2 5 \times 1 0 ^ { 7 }
$$

与-58,125,000相等。而

$$
- 5 . 8 1 2 5 { \times } 1 0 ^ { - 7 }
$$

则与

$$
- 0 . 0 0 0 , 0 0 0 , 0 5 8 , 1 2 5
$$

相等。

在计算机中，对于小数的存储方式，除了定点格式外还有另外一种选择，它被称做浮点格式（floating-point notation)。因为浮点格式是基于科学计数法的，所以它是存储极大或极小数的理想方式。但计算机中的浮点格式是借助二进制数实现的科学计数法形式，因此我们首先要了解如何用二进制表示小数。

实际操作起来比预想的要简单。在十进制数中，小数点右边的数字与10的负整数次幂相关联；而在二进制数中，二进制小数点（就是一个简单的句点，看起来同十进制小数点一样）右边的数字和2的负整数次幂相关。例如，下面这个二进制数：

23 定点数和浮点数

371

## PDF_PHYSICAL_PAGE_INDEX=388 | PRINTED_PAGE=372

$$
1 0 1 . 1 1 0 1
$$

可以用如下方式转换为十进制数：

$$
\begin{array} { l } { { 1 \times 4 + } } \\ { { \ 0 \times 2 + } } \\ { { \ 1 \times 1 + } } \\ { { \ 1 \div 2 + } } \\ { { \ 1 \div 4 + } } \\ { { \ 0 \cdot 8 + } } \\ { { \ 1 \div 1 6 } } \end{array}
$$

将乘数和除数用2的整数次幂替换，就可以替换除号：

$$
\begin{array} { r l r } & { \mathbf { l } \times 2 ^ { 2 } + } & \\ & { \mathbf { 0 } \times 2 ^ { 1 } + } & \\ & { \mathbf { l } \times 2 ^ { 0 } + } & \\ & { \mathbf { l } \times 2 ^ { - 1 } + } & \\ & { \mathbf { l } \times 2 ^ { - 2 } + } & \\ & { \mathbf { 0 } \times 2 ^ { - 3 } + } & \\ & { \mathbf { l } \times 2 ^ { - 4 } } & \end{array}
$$

2的负整数次幂等于从1开始反复除以2，上式可以改写为：

$$
\begin{array} { l } { { 1 { \times } 4 + } } \\ { { 0 { \times } 2 + } } \\ { { 1 { \times } 1 + } } \\ { { 1 { \times } 0 . 5 + } } \\ { { 1 { \times } 0 . 2 5 + } } \\ { { 0 { \times } 0 . 1 2 5 + } } \\ { { 1 { \times } 0 . 0 6 2 5 } } \end{array}
$$

经过这种计算，101.1101与十进制数5.8125是相等的。

在十进制的科学计数法中，规范化式的有效数应该大于或等于1且小于10；类似的，

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

372

## PDF_PHYSICAL_PAGE_INDEX=389 | PRINTED_PAGE=373

在二进制的科学计数法中，规范化式的有效数应该大于或等于1且小于10（即十进制的2)。因此，在二进制的科学计数法中，下面这个数字：

$$
1 0 1 . 1 1 0 1
$$

其规范化式应该是：

$$
1 . 0 1 1 1 0 1 \times 2 ^ { 2 }
$$

这个规则暗示了这样一个有趣的现象：在规范化二进制浮点数中，小数点的左边通常只有一个1，除此之外没有其他数字。

当代大部分计算机和计算机程序在处理浮点数时所遵循的标准是由IEEE(Institute ofElectrical and Electronics Engineers，美国电气和电子工程师协会)于1985年制定的，ANSI(American National Standards Institute，美国国家标准局)也认可该标准。ANSI/IEEE Std754-1985 称作 IEEE 二进制浮点数算术运算标准（IEEE Standard for Binary Floating-PointArithmetic）——它只有18页——相对于其他标准来说是非常简短了，但却奠定了以简便方式编码二进制浮点数的基石。

IEEE浮点数标准定义了两种基本的格式：以4个字节表示的单精度格式和以8个字节表示的双精度格式。

让我们首先来了解一下单精度格式。它的4个字节可以分为三个部分：1位的符号位(0代表正数，1代表负数)，8位用做指数，最后的23位用做有效数。下表给出了单精度格式的三部分的划分方式，其中有效数的最低位在最右边。

$$
\boxed { \begin{array} { c c } { s = 1 \textcircled { 1 } \acute { 2 } \acute { 1 } \acute { 9 } \acute { 5 } \boxed { \begin{array} { c c } { e = 8 \left. \acute { 1 } \acute { 2 } \acute { 1 } \acute { 9 } \acute { 3 } \acute { 5 } \acute { 5 } \right. } \\ { \left. \acute { 1 } \acute { 3 } \acute { 2 } \acute { 1 } \acute { 5 } \acute { 3 } \acute { 5 } \right. } \end{array} } } \end{array} } \left| \begin{array} { c c } { e = 8 \left. \acute { 1 } \acute { 2 } \acute { 1 } \acute { 5 } \acute { 2 } \acute { 5 } \acute { 5 } \acute { 5 } \acute { 5 } \right. } \\ { e = 2 3 \textcircled { 1 } \acute { 2 } \acute { 7 } \acute { 1 } \acute { 3 } \acute { 5 } \acute { 5 } \acute { 3 } \acute { 5 } \acute { 5 } \right. } \end{array} 
$$

三部分共32位，也就是4个字节。我们刚才提到过，对于二进制科学计数法的规范化式，其有效数的小数点左边有且仅有一个1，因此在IEEE浮点数标准中，这一位没有分配存储空间。在该标准中，仅存储有效数的23位小数部分，尽管存储的只有23位，但仍然称其精度为24位。我们将在下面的内容里体会24位精度的含义。

8位指数部分的取值范围是0\~255，称为偏移（biased）指数，它的意思是：对于有符号指数，为了确定其实际所代表的值必须从指数中减去一个值—称做偏移量（bias)。对于单精度浮点数，其偏移量为127。

23 定点数和浮点数

373

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