• 欢迎访问我的个人博客网站,本站专注电商运营,建站SEO推广服务,欢迎大家的访问,有需求可以与我联系!

第三章 计算机的算术运算

加法是计算机中必备的操作。数据从右到左逐位相加,同时进位也相应地向左传播,就如手动计算一样。减法也可采用加法实现:减数在简单的取反后再进行加法操作。

定点数的加减运算包括原码、补码和反码 3 种带符号数的加减运算,其中补码加减运算实现起来最方便。

一、原码加减运算

对两个数进行加减运算时,计算机的实际操作是加还是减,不仅取决于指令的操作码,还取决于两个操作数的符号,当原码加减运算时,符号位并不参与运算,只有两数的绝对值参加运算。首先要判断参加运算的两个操作数的符号,再根据操作的要求决定进行相加还是相减运算,最后还要根据两个操作数绝对值的大小决定结果的符号,整个运算过程比较复杂。例如:加法运算时由于两数异号可能要做减法;减法运算时又由于两数异号可能做加法。

 3.1.2 溢出与检测方法

硬件规模总是有一定限制的,如字宽只有 32 位。当运算结果超过这个限制时,就会发生溢出。

一、溢出概念

在补码加减运算中,有时会遇到这样的情况:两个正数相加,而结果的符号位却为 1(结果为负);两个负数相加,而结果的符号位却为 0 (结果为正) 。以字长为 8 位的定点整数的加法运算举例如下:

两负数相加结果为 100D,显然也是错误的。正确计算结果应该等于-156。

为什么会发生这种错误呢?原因在于两数相加之和的数值已超过了机器允许的表示范围。8 位补码表示数值的范围是(-128)-(+127),而-156 已经超出了这个范围,因而产生溢出。

三、溢出检测方法

(一)单符号位的判溢

两操作数同号且和数的符号与操作数的符号不同。

在定点机中当运算结果发生溢出时,机器通过逻辑电路自动检查出溢出,并进行异常处理。

上面介绍了如何检测计算机中的二进制补码操作的溢出,但无符号整数的溢出情况是如何的呢?由于无符号数通常用于表示内存地址,这种情况下的溢出可以忽略。

因此,计算机设计者必须提供一种方法,能够在某些情况下忽略溢出的发生,而在另一些情况下则能进行溢出的检测。MIPS 采用两种类型的算术指令来解决这个问题:

·加法( add)、立即数加法(addi)和减法(sub),这三条指令在溢出时产生异常。

·无符号加法( addu)、立即数无符号加法(addiu)和无符号减法(subu),这三条指令在发生溢出时不会产生异常。

因为 C 语言忽略溢出,所以 MIPS C 编译器总是采用无符号的算术指令 addu、addiu 和 subu,而不必考虑变量的类型。

3.1.3 算术逻辑单元的硬件实现

算术逻辑单元(ALU)是计算机的主要功能部件,执行加、减等算术运算以及与、或等逻辑运算。我们使用四种硬件模块(与、或门、反相器、多路选择器)构建 ALU。

因为 MIPS 中字长 32 位,需要 32 位宽 ALU。假定需要的 ALU 通过 32 个 1 位加法器连接构成。因此,首先来看 1 位 ALU 的构建 ALU 包括的下一个功能是加法器。

加法器必须有两个操作数输入以及一个和(sum)的 1 位输出。这里还必须有第二个输出用于进位,称为进位输出(CarryOut)。因为来自邻近加法器的进位输出要作为加法器的输入,所以还要有第三个输入,称为进位输入(CarryIn)。

32 位 ALU

既然已经完成了 1 位 ALU,只要将邻近的“黑盒子”连接起来就能创建完整的 32 位 ALU

二进制补码加法器硬件设计的简化可以解释为什么二进制补码表示成为整数计算机算法的普遍标准

三、将 32 位 ALU 修改成 MIPS 的 ALU

    大部分计算机的 ALU 都包括加、减、与、或四种操作,大部分 MIPS 指令操作都可以用这个 ALU 完成,但是这个 ALU 的设计是不完整的。

    还需要支持的一个指令是小于置位指令(slt)。如果 rs< rt,这个操作结果为 1,否则为 0。因此,slt 将除最低位外所有位置为 0,最低位根据比较的结果置位。为了使 ALU 能够执行 slt,首先需要扩展图 3.1.3-7 中的三输入多路选择器,为 slt 结果增加一个输入,称这个新输入为 Less 并且只供 slt 使用。

 除了最低位,Less 输入连接到 0。最低位连接到最高位设置输出。如果 ALU 执行 a-b 并且多路选择器选 3。若 a<b,结果为 0 …0001,否则为 0…000。

进行小于比较操作时如果 a<b,最低位需要被置为 1。也就是说,如果 a-b 是负数最低位置为 1,如果 a-b 是正数最低位置为 0。需要的结果恰好与值的符号位完全相同:1 意味着负数,0 表示正数。根据这个结果,只需要将加法器输出的符号位连接到最低位就可以实现小于比较。

不幸的是,图 3.1.3-8 的图中对于 slt 操作的来自 ALU 最高位的结果输出不是加法器的输出;对于 slt 操作的 ALU 输出显然是 Less 的输入值。

因此,需要新的 1 位 ALU 以使最高位具有额外输出位:加法器的输出。图 3.1.3-9 给出了这样的设计,新的加法器输出线称为 Set.只供 slt 使用。只要特殊的 ALU 需要最高位,就增加溢出检测逻辑,因为这也与最高位有关。

注意:每次使用 ALU 进行减法运算时,将 Carryln 和 Binvert 都置为 1。对于加法和逻辑运算,两条控制线都置为 0。因此通过将 Carryln 和 Binvert 合并成一条控制线 Bnegate,可以简化 ALU 的控制。

为了进一步将这个 ALU 修改为 MIPS 的 ALU,还必须支持条件分支指令。这些指令在两个寄存器相等或者不相等时分支。使用 ALU 进行相等测试的最简单方法是计算 a-b.然后测试结果是否为 0,因此,如果增加硬件测试结果是否为 0,就可以测试相等性。最简单的办法是将所有输出进行或运算然后通过反相器发送信号:

我们只用十进制数中的 0 和 1 来作为例子,计算 100010 乘以 100110:

第一个源操作数称为被乘数,第二个源操作数称为乘数,最终的结果称为积。乘法规则:每次从右到左选取乘数的一位,乘以被乘数,然后相对上一个中间积,将当前积左移一位。

可以观察到,积的位数远远大于被乘数和乘数。在这个例子中,我们只使用了十进制中的 0 和 1。因为只有两个选择,所以每一步的乘法都很简单:

1)当乘数位为 1 时,只需要将被乘数(1×被乘数)复制到合适的位置。

2)当乘数位为 0 时,将 0(0×被乘数)放置到合适的位置。

虽然上面十进制的例子是限制使用了 0 和 1,但二进制数的乘法只能使用 0 和 1,因此也只有这两种选择。

图 3.1.4-2 乘法硬件结构

被乘数寄存器、ALU 和积寄存器都是 64 位长,而乘数寄存器为 32 位长。32 位的被乘数在开始时放置在被乘数寄存器的右半部分,然后每次左移一位。乘数则每次向相反的方向移动。算法开始时,积被初始化为 0。控制逻辑决定何时对被乘数和乘数寄存器进行移位,以及何时将新值写入积寄存器。

假设乘数放置在 32 位的乘数寄存器中,64 位的积寄存器被初始化为 0。从采用纸和笔计算的方法中,我们可以清楚地看到被乘数在每步需要左移一位,因为它需要与前面的中间结果相加。在经过 32 步后,32 位长的被乘数将要左移 32 位。因此,我们还需要一个 64 位的被乘数寄存器,且在初始化时 32 位的被乘数放在右半部分,左半部分清 0。然后,每执行一步,这个寄存器中的值就左移一位,将被乘数与 64 位积寄存器中的中间结果对齐并累加到中间结果。

给出了对于操作数的每 1 位的三个基本执行步骤。乘数的最低位(乘数的第 0 位)决定了被乘数是否被加到积寄存器上。第二步中的左移起着将被乘数左移的作用,就如同用纸和笔做乘法一样。第三步中的右移给出了下一个迭代中要用的乘教位。这三个步骤要重复执行 32 次来获得积。如果每步需要一个时钟周期,这个算法将需要大概 100 个时钟周期来完成两个 32 位的数相乘。像乘法这样的算术操作的相对重要性因程序而异,一般加法和减法出现的次数要比乘法频繁 5-100 倍。因此,在许多应用程序中多步乘法不会显著影响性能。但 Amdahl 定律告诉我们如果一个慢速操作在程序中占据一定比重的话,也会限制程序的性能。

这个算法和硬件结构可以很容易改进成每一步只需要一个时钟周期。这些操作以并行化来加速执行:当乘数位为 1 时,将乘数和被乘数进行移位,同时将被乘数和积相加。这时需要保证硬件测试的是乘数最右边的位,而且得到的是被乘数移位前的值。

被乘数寄存器、ALU、乘敷寄存器都是 32 位长,只有积寄存器是 64 位长。现在将积进行右移,单独的乘数寄存器也撤销了。乘数放在积寄存器的右半部分。

当乘数为常数时,乘法也可以用移位来替代。一些编译器将有短常数的乘法替换为一系列的移位和加法。因为左移一位就是将一个数放大两倍,左移和乘以 2 为底的指数有着等同的效果。

二、有符号乘法

到目前为止,我们处理的对象都是正数。对于理解如何处理有符号乘法,最简单的方法是首先将被乘数和乘数转化为正数,并记住原来的符号位。这样,就可用上述最后的算法迭代 31 次,符号位不必参与运算。当符号相异时,积为负。

三、MIPS 中的乘法

MIPS 提供了一对单独的 32 位寄存器来容纳 64 位的积,称为 Hi 和 Lo。为了产生正确的有符号积和无符号积,MIPS 提供了两个指令:乘法(mult)和无符号乘法(multu)。为了取得 32 位的整数积,程序员需要使用 mflo 指令(move from Io)。 MIPS 汇编器为乘法生成了一条伪指令,它使用了三个通用寄存器,用 mflo 和 mfhi 指令将积送人指定的寄存器。

乘法硬件只是简单的移位和加法,其算法类似于采用纸和笔的计算方法。编译器甚至会用移位指令来代替乘数为 2 的幂次的乘法操作。MIPS 乘法指令都忽略溢出,所以要由软件来检测是否因积过大而不能被 32 位所表示。对于 multu 指令,如果 Hi 为 0 则无溢出;对于 mult 指令,如果 Hi 为 Lo 的符号位则也无溢出。可以使用指令 mfhi(move from hi)将 Hi 的值移入一个通用寄存器来检测溢出。

3.1.5 除法

和乘法相反的操作是除法,它用得较少,但很诡异。它甚至可能会出现数学上的无效操作:除数为 0。

首先通过十进制数的长除法来回忆一下操作数的命名和小学时学习的除法算法。类似于乘法,我们只使用十进制中的 0 和 1。这个例子是计算 1 00101010 除以 100010:

和乘法相反的操作是除法,它用得较少,但很诡异。它甚至可能会出现数学上的无效操作:除数为 0。

首先通过十进制数的长除法来回忆一下操作数的命名和小学时学习的除法算法。类似于乘法,我们只使用十进制中的 0 和 1。这个例子是计算 1 00101010 除以 100010:

第三章 计算机的算术运算

图 3.1.5-1 长除法例

除法中的两个源操作数,称为被除数和除数,结果称为商,还有一个第二结果,称为余数。这里用一种方式来表达它们之间的关系:

被除数=商×除数+余数

这里余数要小于除数。在某些场合,程序使用除法指令只是为了获得余数,而忽视商。

这个过程中每次都尝试看最大能减掉多少,然后以此产生商。我们小心地选择出只用 0 和 1 的十进制例子,从而很容易刿断出需要将多少倍的除数从被除数中减去:要么是 1 倍,要么是 0 倍。二进制数仅包含 0 和 1,所以二进制除法也仅有这两种选择,从而简化了二进制除法。

现在我们假设被除数和除数都为正,因此商和余数也都非负。除法的源操作数和两个结果都是 32 位宽,我们暂且忽略符号位。

一、除法算法及其硬件结构

图 3.1.5-2 给出了模拟小学学过的除法算法的硬件结构。在开始时,32 位的商寄存器设为 0。算法每次的迭代将除数向右移一位。所以我们需要在开始时将除数放置在 64 位除数寄存器的左半边,然后每次右移一位来和被除数对齐。余数寄存器初始化为被除数。

图 3.1.5-3 给出了第一种除法算法的三个步骤。不像人那样聪明,计算机不可能提前知道除数是否小于被除数。所以需要在第一步中减去除数;如果结果为正,则除数小于等于被除数,所以我们取商为 1(第 2a 步)。如果结果为负,则通过将除数加到余数来恢复上一次的值,然后取商为 0(第 2b 步)。除数右移,然后再次迭代。迭代完成后,余数和商存放在以它们命名的寄存器中。

除数寄存器、ALU、余数寄存器都是 64 位宽,只有商寄存器是 32 位宽。32 位的除数开始放置在除数寄存器的左半部分,然后每次迭代右移一位。余数寄存器初始化为被除散。控制逻辑决定何时对除数和商寄存器进行移位以及何时将新值写入余数寄存器。

如果余数为正,则将除数从被除数中减去,然后在第 2a 步取商为 1。如果第 1 步之后余数为负;则意味着除数不能从被除数中减去,所以在第 2b 步中商 0 并将除数加到余数上,即做第 1 步减法的逆操作。在第 3 步,进行最后的移位,根据下一个迭代的被除散,将除数适当对齐。这些步骤将要重复 33 次。

除数寄存器、ALU、商寄存器都是 32 位,只有余数寄存器为 64 位。同图 3.1.5-2 相比,ALU 和除数寄存器都是位宽减半,余数进行左移。这个结构将商寄存器和余数寄存器的右半部分进行了拼接。

二、有符号除法

到目前为止,我们一直忽略有符号敷的除法。最简单的办法是记住除数和被除数的符号。如果两者的符号相异,则商为负。

三、MIPS 中的除法

你可能已经注意到相同的顺序执行硬件结构既可以做乘法,又可以做除法。唯一需要的是一个 64 位的可左右移位的寄存器和一个能做加减法的 32 位宽的 ALU。因此,MIPS 用 32 位的 Hi 和 32 位的 Lo 寄存器来处理乘法和除法。我们从上面的算法中可能已经猜出,在除法指令执行完后,Hi 存放着余数,Lo 存放着商。

为了处理有符号整数和无符号整数,MIPS 采用两条指令:除(div)和无符号除(divu)。MIPS 汇编器允许除法指令使用三个寄存器,且采用 mflo 和 mfhi 指令将运算结果放入指定的通用寄存器

3.2.1 浮点数的表示

3.2.1 浮点数的表示

在科学计算中,常常会遇到非常大或非常小的数值,如电子的质量(9×10-28 克)和太阳的质量(2×1033 克)相差甚远,在定点计算机中无法直接来表示这个数值范围。要使它们送入定点计算机进行某种运算,必须对它们分别取不同的比例因子,使其数值部分绝对值小于 1,即:

9 × 10-28=0.9 × 10-27

2 × 1033=0.2 × 1034

这里的比例因子 10-27 和 1034 要分别存放在机器的某个存储单元中,以便以后对计算结果按这个比例增大。显然这要占用一定的存储空间和运算时间。

除了有符号和无符号整数,编程语言也支持带小数的效字,即数学上的实数。如:

    3.14159265…10  (Pi)

    2. 71828…10  (e)

    0. 000000001 即 1.010×10-9(纳秒级)

    3 155 760 00010  即 3.155 7610×109(一百年的秒数)

请注意,在最后的例子中,那个数并不是小数,而是比 32 位的有符号整数还要大的数。这种表达上述例子中后两个数的记数法称为科学记数法。一个采用科学计数法表示的数,若没有前导零且小数点左边只有一位整数,可称为规格化数。例如.1. 010×10-9 就是规格化的科学计数,但 0.1×10-8 和 10.0×10-10 就不是。

正如可以用科学记数法来表示十进制数那样,我们也可以用科学记数法来表示二进制数,如:

    1. 02 x2-1

为了使二进制数规格化,需要定义一个基数,这个基数可用来移位使小数点左边只保留一位非零数。只有基数为 2 才满足要求。因为基数不是 10,所以我们称这时的小数点为二进制小数点。

这类计算机算术称为浮点计算,因为其表示的二进制小数点是不固定的,与整数相似。正如科学记数那样,数被表示为二进制小数点左边只有一位非零数的形式。在二进制中,其格式为:

1.xxxxxxxxx2×2YYYY

(尽管计算机对指数也同其他数一样表示为以 2 为基的形式,但这里为了简化记数,我们用十进制来表示指数)

对实数采用规格化形式的标准科学记数法有三个优点:简化了浮点数的数据交换;简化了浮点算术算法;提高了用一个字存储的数的精度,因为无用的前导零可能占用的位被二进制小数点右边的有效位替代了。

浮点表示的设计者必须在尾数位宽和指数位宽之间找出折中的办法,因为字的大小是固定的,有一部分增加一位,则另一部分就要减少一位。折中是在精度和表示范围间进行权衡:增加小数部分会增加表示精度,而增加指数部分会增加数的表示范围。正如我们在第 2 章中所提到的设计方针讲的那样,好的设计需要好的折中。

浮点数通常是多个字的宽度。MIPS 中的浮点数表示如表 3.2.1-1:s 为浮点数的符号(1 表示负数),指数域为 8 位宽(包括指数的符号位),尾数域为 23 位宽。这种表示称为符号和数值(sign and magnitude,也是常常说的原码表示),因为符号和数值的位置是相互分离的。

表 3.2.1-1 单精度浮点数格式

第三章 计算机的算术运算

一般浮点数表示为这样的形式:F 为小数域的值,E 为指数域的值。

    (-1)S×F×2E

浮点数表示法使 MIPS 计算机有很大的数值表示范围,可以小到 2.010×10-38,大到 2.010×1038。

但它和无穷还是不同的,所以依然有可能会因数太大而不能表示。因此,正如在整数运算中那样,溢出中断在浮点运算中也会发生。注意,这里的溢出(上溢)表示数太大而不能在指数域表示。

浮点也会出现一种新的异常事件。正如程序员想要知道何时他们计算的数太大而不能表示那样,他们同样也想知道一个非零的小数是否会太小而不能表示;任何一种事件都会引起程序大而不能在指数域中表示出来。

一种减少上溢和下溢的方法是采用更大指数的格式。在 C 语言中称为 double,基于 double 的操作称为双精度浮点算术;单精度浮点就是前面的格式。

双精度浮点数占用了两个 MIPS 字,如表 3.2.1-2 下所示。其中,s 表示符号,指数域为 11 位,尾数域为 52 位。

表 3.2.1-2 双精度浮点数格式

第三章 计算机的算术运算

MIPS 双精度的表示范围从 2.02×10-308 到 2.02×10308。尽管双精度增加了指数范围,它主要的优势还是通过提供更多的有效位数来实现更大的表示精度。

实际上它们是 IEEE 754 浮点标准的一部分,从 1980 年以来的每台计算机都遵循该标准。该标准既简化了浮点程序的接口,又提高了计算机算术的质量。

为了将更多的数据位打包到有效位数(significand)部分,IEEE 754 甚至隐藏了规格化二进制数的前导位 1。因此,在单精度下,数有 24 位宽(隐含的 1 和 23 位尾数),在双精度下为 53 位宽(1+52)。为了精确,我们用术语有效位数来表示 24 位或者 53 位的数,就是隐含 1 加尾数。因为 0 没有前导位 1,它的指数保留为 0,所以硬件就不会将前导位 1 加到尾数上面。

因此 0… 002 代表 0;其他数的表示依然采用前面的形式,就是加上了隐含 1:

(-1)s×(1+F)×2E

其中,F 表示的是 0 和 1 之间的数,E 表示的是指数域中的值。

第三章 计算机的算术运算

图 3.2.1 IEEE 754 浮点数的编码

图 3.2.1 给出了 IEEE 754 浮点数的编码。 IEEE 754 标准的其他特点是用特殊的符号来表示异常事件。如软件可以将结果设置成某种格式来表示+∞或者-∞,以替代除 0 中断;最大的指数保留下来标识那些特殊符号。

IEEE 754 给出了一种表示无效操作结果(如 0/0 或者无穷减无穷)的符号 NaN (Not a Number)。即非数的意思。设立 NaN 的目的是为了让程序员推迟程序中的一些测试和决定,等到方便的时候才进行。

IEEE754 的设计者还希望浮点表示能够容易地处理整数比较,特别是排序的时候。这就是为什么符号放在最高位的原因,这样就可以快速地测试出小于、大于、等于 0 的情况。(比起简单的整数分类,它稍显复杂,因为这种记数法本质上是符号和数值的形式,而不是补码形式)

将指数放在有效数前也能简化用整数比较指令做的浮点数分类,因为在有着相同的符号的情况下,指数大的数其值就大。

负的指数对简化分类形成一个挑战。如果我们用补码或者其他的记数法,可能会使负指数的高位为 1,从而使一个负指数显得是一个大数了。

因此希望记数法能将最小的负指数表示为 00…002,而最大的正指数表示为 11—112。这种记数法称为带偏阶的记数法(biased notation)。需要从带偏阶的指数中减去偏阶,才能获得真实的值。

IEEE 754 规定单精度的偏阶为 127,所以指数为-1,则会表示为-1 +12710,即 12610=011111102,而+1 表示为 1+127,即 12810=1000 00002 双精度的指数偏阶为 102310 给指数带偏阶后,浮点数表示为

    (-1)s×(1+Fraction)×2(Exponent-Bias)

从而,单精度数的表示范围从

    ±1. 0000 0000 0000 0000 0000 0002×2-126

    ±1. 11111111 1111 1111 1111 1112 x2+127

【例】已知一个 IEEE754 的单精度机器数为 E0C80000H,求它所表示的十进制数的真值是多少?

解答:

E0C80000H=1 110 0000 1100 1000 0000 0000 0000 00002

从单精度的格式可见 S=1,E=193,M=0.5625

所以:e=E–127=193–127=66,则所表示的十进制数的真值是:

N=(–1)1×266 ×(1+0.5625) =–266 ×(1.5625)

3.2.2 浮点加减运算

浮点数的运算包括阶和尾数两部分的运算。 执行浮点数的加减运算,需要经过对阶、尾数加/减、尾数结果规格化等步骤。

一、对阶

使两个浮点数阶码取得一致的过程。两个浮点数相加或相减,首先要把小数点的位置对齐,而浮点数的小数点的实际位置取决于阶码的大小,因此,对齐两数的小数点,就是使两数的阶码相等,这个过程称为对阶。

对阶原则:小阶向大阶看齐。采用这一规则的原因是当阶码小的数的尾数右移并相应增加阶码时,舍去的仅是尾数低位部分,误差比较小。要使小阶的阶码增大,则相应的尾数右移,直到两数的阶码相等为止。尾数每右移一位,阶码加 1。

二、尾数相加减

其算法与前面介绍的定点加/减法相同。

三、结果规格化

尾数加/减运算之后得到的数可能不是规格化数,必须进行结果规格化操作。因此,在加法后,我们可能需要对和移位,将其变为规格化形式,同时相应地调整指数。但是如果一个数为正,一个数为负,则和可能会有许多前导 0,从而需要左移。无论指数是增加还是减少,都需要检查上溢或者下溢,必须保证指数能够被固定位宽的指数域所表示。

四、溢出及其处理

与定点加减法一样,浮点加减运算最后一步也需判溢出。当尾数出现 10.xxx…x 或 01.xxx…x 时,并不表示溢出,只有将此数右规后,再根据阶码来判断浮点运算结果是否溢出。浮点数的溢出情况由阶码的符号决定,若结果的阶正上溢,则将溢出标志置1,通知控制部件,转中断处理或停机。

五、舍入

尾数右移时,为减少误差,需进行舍入处理。

由于受到硬件的限制,在对阶和右规处理之后有可能将尾数的低位丢失,这会引起一些误差。舍人方法有很多种,最简单的是恒舍法,即无条件的丢掉正常尾数最低位之后的全部数值。

IEEE754 标准中,提供四种可选的舍入处理法: 就近舍入,朝 0 舍入,朝+∞舍入,朝-∞舍入。

3.2.3 浮点乘法运算

两浮点数相乘,其乘积的阶码应为相乘两数的阶码之和,其乘积的尾数应为相乘两数的尾数之积。

第三章 计算机的算术运算

第三章 计算机的算术运算

则:

第三章 计算机的算术运算

浮点乘法运算的规则是:乘积的尾数是相乘两数的尾数之积,乘积的阶码是相乘两数的阶码之和。当然,这里也有规格化与舍入等步骤。

浮点乘法运算包括以下五个步骤:

步骤 1:将两个带偏阶的指数相加,然后减去一个偏阶,获得一个新的偏阶。

步骤 2:将有效数相乘。

步骤 3:这个积是未规格化的,所以我们需要规格化积它。积需要右移一位来变成规格化形式,同时指数加 1。此刻,我们要检查上溢和下溢。当两个源操作数都很小时,两者都有非常大的负指数时,就有可能发生下溢。

步骤 4:对结果进行舍人。

步骤 5:积的符号取决于原始源操作数的符号。当它们相同时,符号为正;否则,符号为负。

3.3 MIPS 浮点运算指令

MIPS 有如下指令来支持 IEEE 754 的单精度和双精度格式:

·浮点单精度加(add.s)和双精度加(add.d)

·浮点单精度减(sub.s)和双精度减(sub.d)

·浮点单精度乘(mul.s)和双精度乘(mul.d)

·浮点单精度除(div.s)和双精度除(div.d)

·浮点单精度比较(c.x.s)和双精度比较(c.x.d),其中,x 可能是等于(eq)、不等于(neq)、小于(lt)、小于等于(le)、大于(gt)或大于等于(ge)

·浮点比较为真跳转(bclt)和浮点比较为假跳转(bclf)

浮点比较根据比较条件,将比较结果设为真或者假,然后浮点跳转根据比较结果条件决定是否跳转。

MIPS 设计增加了单独的浮点寄存器,称为$f0,$f1,$f2,…——用于单精度及双精度。因此,也有单独的针对浮点寄存器的存和取指令:lwc1 和 swc1。浮点数据传送的基寄存器仍然采用整数寄存器。

双精度寄存器是一组单精度寄存器的偶数一奇数对,并使用偶数寄存器编号作为其名称。因此,一对单精度寄存器$f2 和$f3 形成一个双精度寄存器,称为$f2。

存储程序计算机有一个副作用,就是位模式没有内在的含义。相同的位模式可能代表有符号整数、无符号整数、浮点数和指令等。对这个字操作的指令决定了其意义。

计算机算术和用纸和笔手算的算术不同的地方是受到有限精度的约束。这个限制可能会因为计算中数大于或者小于预先的设定而导致无效操作。这种异常,称为“上溢”或“下溢”,可能导致异常、中断或类似于意外的子程序调用。

浮点算术因为是对实际的数字的近似而增加了挑战性,而且要小心确保所选的计算机数能够最接近地表示实际数字。不精确和有限的表达带来的挑战是数值分析领域灵感的部分来源。

过去的多年里,计算机算术变得非常标准化,极大地增加了程序的可移植性。当今大量计算机都采用了二进制整数补码算术和 IEEE 754 二进制浮点算术。