02 Representing and Manipulating Information
计算机将信息按位编码,通常组织成字节序列。用不同的编码方式表示整数、实数和字符串。不同的计算机模型在编码数字和多字节数据中的字节排序时使用不同的约定。
C语言的设计可以包容多种不同字长和数字编码的实现。虽然高端机器逐渐使用64位字长,但目前大多数机器仍使用32位字长。大多数机器对整数使用补码编码,而对浮点数使用IEEE浮点编码。在位级上理解这些编码,并且理解算术运算的数学特性,对于想使编写的程序能在全部数值范围上正确运算的程序员来说,是很重的。
在相同长度的无符号和有符号整数之间进行强制类型转换时,大多数C语言实现遵循的原则是底层的位模式不变。在补码机器上,对于一个w位的值,这种行为是由函数T2Uw和U2Tw来描述的。C语言隐式的强制类型转换会出现许多程序员无法预计的结果,常常导致程序的错误。
由于编码的长度有限,与传统整数和实数运算相比,计算机运算具有完全不同的属性。当超出表示范围时,有限长度能够引起数值溢出。当浮点数非常接近于0.0,从而转换成零时,也会下溢。
和大多数其他语言一样,C语言实现的有限整数运算和真实的整数运算相比,有一些特殊的属性。例如,由于溢出,表达式x*x能够得出负数。但是,无符号数和补码的运算都满足整数运算的许多其他特性,包括结合律、交换律和分配律。这都允许编译器做很多的优化。例如,用(x<<3)-x取代表达式7*x时,我们就利用了结合律、交换律和分配律的属性,还利用了移位和乘以2的幂之间的关系。
我们已看到了几种使用位级运算和算术运算结合的聪明方法。例如,使用补码运算,~x+1等价于-x。另一个例子,假设我们想要一个形如[0,…,0,1,…1]的位模式,由于w-k个0后面紧跟着k个1组成。这些模式有助于掩码运算。这种模式能够通过C表达式(1<<k)-1生成,利用的是这样一个属性,即我们想要的位模式的数值为2k-1。例如,表达式(1<<8)-1将产生位模式0xFF。
浮点数表示通过将数字编码为x*2y的形式来近似地表示实数。最常见的浮点数表示方式是由IEEE标准754定义的。他提供了几种不同的精度,最常见的是单精度(32位)和双精度(64位)。IEEE浮点也能够表示特殊值+∞、-∞和NaN。
必须非常小心地使用浮点数运算,因为浮点数运算只有有限的范围和精度,而且不遵守普遍的算术属性,比如结合性。