深入理解计算机系统 第二章 表示和操作信息

现代系统存储和处理的信息通过二进制表示的。二进制,或者称为位(bit),形成了数字革命的基础。十进制(以10为基底)已经使用了超过一千年,它形成于印度, 在十二世纪被阿拉伯数学家改进,十三世纪被意大利数学家斐波那契带到西方。对于有十个手指头的人来说,使用十进制是很自然的,但是对于存储和处理信息的机器来说,二进制是更合适的。二进制可以很方便地被表示、存储和传送,例如,在一个打孔卡上是否有洞,电线上的高低电压,或者顺时针方向或者逆时针方向的磁畴。对二值信号的存储和计算的电路是很简单的和可靠的,这使得制造商可以在单个硅芯片上集成数百万甚至数十亿这样的电路。
单个的位并不是很有用的,但是,当我们同时使用一组位,然后给不同位模式赋予不同的含义,我们就能表示任何有限集合中的所有元素。例如,我们可以用位组表示所有的非负数。通过使用一个标准的字符编码,我们可以编码一个文档中字符和符号。本章中,我们将详细讲解这两个问题,另外还会讲到表示负数和近似表示实数。
我们考虑三种最重要的数字的表示。无符号数字是基于传统的二进制记号,用于表示大于等于0的数。二的补码(也就是通常所说的补码)是表示有符号整数最常用的方式,有符号数是指那些既可能是正数也可能是负数的数。浮点数是实数的科学计数法的二进制表示。计算机用这些不同的表示来实现算数操作,比如加法和乘法,这些操作与在整数和实数上的操作相似。
计算机有有限数量的位去表示一个数,因此,当有些操作的结果太大以至于计算机不能表示时就会导致溢出(overflow)。这可能会导致令人意外的结果。例如,在当今大多数计算机(用32位表示整型数)上,计算下面的表达式:
200 * 300 * 400 * 500
得到的结果是-884,901,888。这违背了整数运算的特性——几个正数相乘居然得到负数。
另一方方面,计算机上的整数运算也满足真实的整数运算的许多特性。例如,乘法满足交换率和结合率,因此,下面这些C表达式的结果都是-884,901,888。
(500 * 400) * (300 * 200)
((500 * 400) * 300) * 200
((200 * 500) * 300) * 400
400 * (200 * (300 * 500))
计算机也许不能产生期望的结果,但是至少它总是前后一致的。
浮点运算有不同的数学特性。几个正的浮点数的乘积总是正数,当溢出的时候会得到一个特殊的值+∞。浮点数在计算机表示中的有限精度导致浮点运算不满足结合律。例如,在大多数机器上,C表达式(3.14+1e20)-1e20的结果是0.0,而3.14+(1e20-
1e20)的结果是3.14。整数运算和浮点运算不同数学特性来源于它们对有限的表示范围的处理方式的不同——整数表示能编码一个相对小的范围的值,但是能做到精准,而浮点表示能编码一个相对更大范围的值,但只是一个近似值。
通过研究数的实际表示方式,我们能够明白能被表示的值的范围,理解不同算数操作的特性。这个理解对写出能在大范围的值上正确工作并且有很好跨平台性的程序是很关键的。正如我们将描述的,计算机算数运算和正常运算的一些微妙的差别已经导致了一些安全漏洞。早些时候,程序bug只有在它们被碰巧触发的时候会给人们带来不便,但是,现在,有大量的黑客利用它们能找到的任何bug来对没有对它们授权的系统进行访问。这使得程序员有更大的责任去理解他们的程序是如何工作的以及什么情况下这些程序会以不期望的方式运行。
计算机用几种不同的二进制表示来编码数值。当你在第三章进入机器级编程时,会需要熟悉这些二进制表示方式。我们将在这章描述这些编码方式和向你展示如何推导出数值表示。
我们通过直接操作数字的位级表示来推导出执行算术运算的几种方式。理解这些技术对于理解由尝试优化算术表示求值的执行效率的编译器产生的机器码是很重要的。
我们对这些知识(material)的处理基于一些核心的数学原则。我们从编码的基本定义开始,然后推导出一些特性,比如可表示的数的范围,这些数的位级表示,以及它们的算术操作的特性。我们相信从这些抽象的概念出发检查这些知识(examine the material)对你是很重要的,因为程序员需要对计算机中的算术运用与更加熟悉的整数和实数运算之间的关系有清晰的认识。
C++程序设计语言构建于C语言之上,它们使用相同的数字表示和操作。这一章讲解的任何对于C的知识对C++同样有效。然而,Java语言对数值表示和操作创建了一些新的标准。C标准被设计为允许比较宽范围的实现,而Java标准在数据格式和编码上是十分具体的。在本章的某些地方,我们将强调被Java支持的表示和操作。

你可能感兴趣的:(信息表示)