Skip to content

2.5 小结

计算机将信息编码为位(比特),通常组织成字节序列。有不同的编码方式用来表示整数、实数和字符串。不同的计算机模型在编码数字和多字节数据中的字节顺序时使用不同的约定。

\(\mathrm{C}\) 语言的设计可以包容多种不同字长和数字编码的实现。64 位字长的机器逐渐普及,并正在取代统治市场长达 30 多年的 32 位机器。由于 64 位机器也可以运行为 32 位机器编译的程序, 我们的重点就放在区分 32 位和 64 位程序,而不是机器本身。64 位程序的优势是可以突破 32 位程序具有的 4 GB 地址限制。

大多数机器对整数使用补码编码, 而对浮点数使用 IEEE 标准 754 编码。在位级上理解这些编码, 并且理解算术运算的数学特性, 对于想使编写的程序能在全部数值范围上正确运算的程序员来说, 是很重要的。

在相同长度的无符号和有符号整数之间进行强制类型转换时,大多数 \(\mathrm{C}\) 语言实现遵循的原则是底层的位模式不变。在补码机器上,对于一个 \(w\) 位的值,这种行为是由函数 \({T 2}{U}_{w}\)\({U 2}{T}_{w}\) 来描述的。 \(\mathrm{C}\) 语言隐式的强制类型转换会出现许多程序员无法预计的结果, 常常导致程序错误。

由于编码的长度有限,与传统整数和实数运算相比,计算机运算具有非常不同的属性。当超出表示范围时,有限长度能够引起数值溢出。当浮点数非常接近于 0.0 ,从而转换成零时,也会下溢。

和大多数其他程序语言一样, \(\mathrm{C}\) 语言实现的有限整数运算和真实的整数运算相比,有一些特殊的属性。例如,由于溢出,表达式 \({\mathrm{x}}^{ \star }\mathrm{x}\) 能够得出负数。但是,无符号数和补码的运算都满足整数运算的许多其他属性,包括结合律、交换律和分配律。这就允许编译器做很多的优化。例如,用 \(\left( {\mathrm{x} < < 3}\right) - \mathrm{x}\) 取代表达式 \(7 \star \mathrm{x}\) 时,我们就利用了结合律、交换律和分配律的属性,还利用了移位和乘以 2 的幂之间的关系。

我们已经看到了几种使用位级运算和算术运算组合的聪明方法。例如,使用补码运算, \(\sim \mathrm{x} + 1\) 等价于 \(- \mathrm{x}\) 。另外一个例子,假设我们想要一个形如 \(\left\lbrack {0,\cdots ,0,1,\cdots ,1}\right\rbrack\) 的位模式,由 \(w - k\) 个 0 后面紧跟着 \(k\) 个 1 组成。这些位模式有助于掩码运算。这种模式能够通过 \(\mathrm{C}\) 表达式 \(\left( {1 < < \mathrm{k}}\right) - 1\) 生成,利用的是这样一个属性,即我们想要的位模式的数值为 \({2}^{k} - 1\) 。例如,表达式 \(\left( {1 < < 8}\right) - 1\) 将产生位模式 0 xFF。

浮点表示通过将数字编码为 \(x \times {2}^{y}\) 的形式来近似地表示实数。最常见的浮点表示方式是由 IEEE 标准 754 定义的。它提供了几种不同的精度, 最常见的是单精度 (32 位) 和双精度 (64 位)。IEEE 浮点也能够表示特殊值 \(+ \infty \text{、} - \infty\)\({NaN}\)

必须非常小心地使用浮点运算, 因为浮点运算只有有限的范围和精度, 而且并不遵守普遍的算术属性, 比如结合性。

Comments