【梳理】计算机组成与设计 第3章 算术 第2节 浮点(内附文档高清截图)
配套教材:
Computer Organization and Design: The Hardware / Software Interface (5th Edition)
这是专业必修课《计算机组成原理》的复习指引。建议将本复习指导与博客中的《简明操作系统原理》配合复习。
在本文的最后附有复习指导的高清截图。需要掌握的概念在文档截图中以蓝色标识,并用可读性更好的字体显示 Linux 命令和代码。代码部分语法高亮。
计算机组成原理不是语言课,本复习指导对用到的编程语言的语法的讲解也不会很细致。如果不知道代码中的一些关键字、指令或函数的具体用法,你应该自行查找相关资料。
第二节 浮点
12、浮点(floating-point)是一种数据格式。之所以这样命名是因为小数点的位置不固定。计算机中采用科学计数法(scientific notation)表示一个浮点数。采用科学计数法的优点有:
(1)简化了包含浮点数的数据交换。
(2)简化了浮点数的算法,因为所有浮点数都始终保持这一种格式。
(3)尽可能增加了数据的精度(相同的存储空间而存储精度更高),因为不必要存储一连串前导0(leading 0)。科学计数法中,没有前导0的数称为规范化(normalized)的数。
13、一个浮点数中,一部分是小数(分数(fraction)),一部分是指数(exponent)。这两部分互相挤占存储空间:其中一项多一位,另一项就少一位。第二章我们已经提过:好的设计要求好的妥协。
浮点数除了有上溢(overflow)的概念以外还有下溢(underflow),分别代表浮点数过大或过小以至于指数部分不能表示。
14、计算机中的浮点数主要有单精度(single precision)和双精度(double precision)两种。它们的格式如下:
单精度和双精度的最高位都是符号,正0负1;单精度的指数部分是8位,分数23位;双精度的指数部分是11位,分数部分是52位。它们都由IEEE-754标准规定。符号位放在最高位,指数位放在分数位之前,能够简化浮点数与整数混合的一组数据比较大小的过程。
上面的格式中,指数和分数部分自然也是二进制的;将能够表示的范围转换成十进制,单精度和双精度能表示的绝对值范围大约分别是:1.18×10-38 ~ 3.40×1038,2.23×10-308 ~ 1.79×10308。
除了0以外,分数部分总是在1到2之间。由于1也算一个有效位,所以有效位数(significand)是指数部分的位数+1。当所有位数都是0时,浮点数表示0。还有专门的值表示正负无穷大和NaN(Not a Number)。
设分数部分为F,指数部分为E,符号位S,指数偏移(exponent bias)B,则一个浮点数的值可以表示为:
(–1)S×(1 + F)×2E–B
对单精度浮点数,B = 127;对双精度浮点数,B = 1023。
有很多数是无法用常见的浮点格式精确表示的,这时它们会被转换成离实际数据最近的浮点数保存。
15、2008年,IEEE修订了IEEE-754标准,称为IEEE-754 2008。相比IEEE-754 1985,新版本的标准增加了对半精度(half precision)和四精度(quadruple precision)的支持,也增加了十进制算术的规定。IBM的大型机已经跟进了十进制算术。目前还没有硬件提供对四精度的支持。
16、在IEEE 754以前,为了扩大浮点数能表示的范围,一些计算机表示浮点数并非采用二进制。例如IBM 360、370大型机采用16进制。也就是说,这种计算机的浮点格式中的指数部分每位移一位,相当于乘或除以16。“规格化”后的16进制浮点数最多可以有3个前导零(前导零的数量达到4的时候才可以修改分数部分和指数部分使得分数部分的最高位非0)。于是,16进制浮点在使用过程中,有效位数部分最多有3个bit需要丢弃(二进制位被前导0占了),这就带来了精度上的大问题。IBM的大型机目前既支持IEEE 754标准,也支持十六进制的浮点格式。
17、浮点数的加法过程如下:
先比较指数,当指数不同时,调整指数和分数部分,使得指数较小的浮点数的指数与指数较大的浮点数的相同。然后就可以将有效位相加。接下来将相加的结果规范化,结合左移右移和指数的加减来调整。下一步检查是否上溢或下溢。如是,抛出异常;如否,对结果进行舍入。如果舍入之后变得不规范化,则重新进行规范化,直到舍入后的结果也规范化为止,然后输出。
现代的许多计算机的CPU中包含专门的浮点处理单元(Float processing unit,FPU)来进行浮点运算。
FPU中有一个小的ALU,用于比较两个指数不同的浮点数谁的指数更大。比较结果控制三个数据选择器,也叫多路选择器(multiplexer,MUX),它们选择较大的指数和两数的有效位,然后根据比较结果进行调整,使得指数相同,然后将有效位在大的ALU中相加,接着对结果进行舍入。
18、浮点数乘法的计算步骤如下:
先将指数加起来(要加回多减去一次的指数偏移),然后把有效位相乘,规范化,再检查是否溢出,然后舍入。如果舍入后并非规范化的浮点数,再次规范化(位移有效位兼加减指数),直到舍入后仍旧规范化为止,计算结果的符号并输出。
19、C / C++和许多语言的二维数组是行优先存储的(row-major order),也就是说存储顺序是a[0][0],a[0][1],a[0][2],……。Fortran则采用列优先存储。
20、为了提高舍入的精度,在浮点数的计算过程中会多留两位有效数字,称为保护位(guard bit)。当需要计算乘法时,两位保护位是必要的:乘法的结果可能会出现一个前导零,这时候因为要进行规范化,有效位会被左移一位,这时候一个保护位就会移到最低有效位(least significant bit,LSB)处,这时候仍然需要另一个位于最低有效位右侧的保护位来进行舍入。ULP(units in the last place)用于衡量浮点误差,代表浮点数可以表示的有效数字有几个低位与实际结果不同。IEEE 754保证计算机在使用单精度和双精度浮点每次进行各种运算时的误差在1.5 ULP以内。
21、IEEE 754规定了四种舍入模式:向正无穷舍入、向负无穷舍入、截断(truncate)、向最近的偶数舍入。一个更公平的方法是随机决定:一半的概率向上舍入,一半的概率向下。IEEE 754规定了这个办法:如果是奇数,进一;如果是偶数,截断。
为了实现向最近偶数舍入,除了保护位以外还多了一位粘滞位(sticky bit)。向最近偶数舍入时,入的条件为guard = 1 and sticky = 1,或,guard = 1 and sticky = 0但精度最低位为1。
22、PowerPC、SPARC64和支持SSE、AVX的架构均支持乘加融合(fused multiply add,FMA)指令。这个指令计算a = a + b×c。IEEE 754-2008也添加了FMA支持。这种算式很常用,因此FMA指令大大提升了性能。而且,FMA只在执行完加法后进行一次舍入,而非在乘法和加法的过程中各舍入一次,也提高了精度。
23、有序比较(ordered compare)和无序比较(unordered compare)的区别是:一次有序比较检查是否两个操作数都不是NaN。一次无序比较检查两个操作数中是否任意一个操作数是NaN。与NaN的比较结果是不确定的(无法确定结果),因此,有序 / 无序比较用来检查是否是这种情况。