J.4 浮点乘法
乘法是最简单的浮点运算,因此先讨论它。二进制浮点数表示为 。由
可知,浮点乘法包含几个部分。首先,用普通整数乘法相乘两个有效数。浮点数以符号与数值分离的形式存储,因此乘法器只需处理无符号数,尽管前面已经看到,Booth 重编码也能方便地处理有符号补码。其次,舍入结果:若有效数是无符号 位数,单精度例如 ,乘积最多有 位,必须舍入为 位。最后,计算新指数;由于指数带偏置,须从两个偏置指数之和中减去偏置。
例题 以下单精度数的乘法如何在二进制中进行?原页列式为:
解答 解包后,两个有效数均为 1.0,乘积也是 1.0,因此结果形式为:
指数由下式计算:
图 J.7 给出的偏置为 ,因此 −127 的补码为 。乘积的偏置指数为
这是十进制 134,对应实际指数 ,与预期一致。
译注:原页例题把两个 8 位指数的末位排进了后面的字段,显示成 7 位指数。按题中数值及解答,完整编码应分别为
1 10000010 000…与0 10000011 000…。
浮点乘法最值得关注的是舍入。图 J.9 展示几种情况;各进制的处理类似,因此采用更便于阅读的十进制。
| 情况 | 精确乘法 | 舍入判断 | 三位有效数字结果 |
|---|---|---|---|
| (a) | 第四位 ,进一 | 8.34 | |
| (b) | ,后面存在非零位,进一 | ||
| (c) | ,进一并产生进位 |
这里 ,所以最终保留最高的三位有效数字,第四位称为舍入位,记作 。若 ,直接保留前三位;若 ,如 (a),就在第三位加 1;若 ,如 (b),还要检查后面的数字,决定直接截断还是加 1。其实只需知道后面是否存在非零位,可以用 sticky 位记录。比较 (a)、(b) 可知,相对于乘积最低位,舍入位可能位于两个不同位置。(c) 说明,加 1 可能产生进位,此时有效数会先变为 10.0。
用图 J.2 的乘法器再加一个 sticky 位,就能直接实现舍入。设 A、B、P 都为 位。相乘得到 (P,A) 中的 位乘积。乘法过程中,前 次移入 A 的位都与 sticky 位作 OR。令 表示 sticky 位,(guard,保护位)为 A 的最高位, 为 A 的次高位,则有两种情况:
- P 的最高位为 0:将 P 左移一位,把 A 的 位移入;A 的其他位无需移动。
- P 的最高位为 1:令 、,并把指数加 1。
现在,若 ,P 就是正确舍入的乘积;若 ,应取 P+1,这里的加 1 指在 P 的最低位加 1;若 ,恰好落在中点,是否进一取决于 P 的最低位。

图中 Product 为乘积;Case (1): Shift needed 表示情况 (1) 需要移位;Case (2): Increment exponent 表示情况 (2) 指数加 1。调整二进制小数点位置时,须给指数加 1 来补偿。rnd 表示舍入位,sticky 表示黏滞信息。
把相同规则用于图 J.9(b) 的十进制例子:乘法后 P=126、A=501,。P 的最高位非零,属于情况 (2),令 ,得到 。这似乎可能是中点,但 表明实际略大于半个末位单位,所以向 P 加 1,得到正确舍入结果。
精确规则取决于舍入模式,见图 J.11。注意,P 非负,保存的是结果的绝对值。更高效的实现可参考 Santoro、Bewick 与 Horowitz [1989]。
| 舍入模式 | 结果非负时加 1 的条件 | 结果为负时加 1 的条件 |
|---|---|---|
| 向 | ||
| 向 | ||
| 向 0 | ||
| 舍入到最近值 |
例题 在二进制、 时,说明算法如何在四种舍入模式下计算 。
解答 ,。有效数的整数乘积为 ,所以 P=0110、A=0100,。P 的最高位为 0,按情况 (1) 左移,变为 1100。结果为负,依图 J.11:
| 模式 | 有效数各位 | 原因 |
|---|---|---|
| 向 | 为真,加 1 | |
| 向 | 不加 1 | |
| 向 0 | 不加 1 | |
| 最近值 | 与 均为假 |
指数为 ,所以除向 舍入得到 外,其余模式均得到 。
舍入结果大到无法表示时,就发生上溢。单精度中,结果指数不小于 128 就会如此。若 为偏置指数,则 ,因此 的范围为 到 ,即 −125 到 381。
原书指出,这个范围可用 9 位表示,因此一种检测办法是在 9 位加法器中计算指数,见习题 J.12。一定要在舍入之后检查上溢,图 J.9(c) 已说明舍入可能改变这一判断。
译注:9 位只有 512 种编码,而上述区间有 507 个值,但不能直接套用普通 9 位有符号补码范围 −256…255,或把负数当普通无符号数。这里的 9 位指数运算须配合适当编码与进位判别;原书把具体检测留给习题 J.12。
非规格化数
由于非规格化数的存在,下溢检查更复杂。单精度结果指数小于 −126,不一定就意味着下溢,因为结果仍可能是可表示的非规格化数。例如,。通常,乘积的无偏置指数低于 −126 时,必须右移乘积并递增指数,直到指数达到 −126。若整个有效数都被移出,就发生下溢。下溢的精确定义更微妙,见 J.7 节。
若一个乘法操作数是非规格化数,有效数带前导 0,乘积也会带前导 0。若乘积指数小于 −126,结果是非规格化数,仍按上法右移并递增指数。若指数大于 −126,结果可能成为规格化数;此时左移乘积并递减指数,直到规格化,或指数降至 −126。
非规格化数是浮点乘法实现中的一个主要障碍,因为它要求乘法器支持本来不需要的可变位数移位。因此,高性能浮点乘法器常常不直接处理非规格化数,而是触发陷阱交由软件处理。有些实际程序即便正常运行也会频繁下溢,它们在需要陷阱处理非规格化数的系统中会慢很多。
还没有讨论操作数为 0 的情况。可以在开始乘法前检查两个操作数,也可以在乘法后检查乘积。如果事后检查,要正确处理 :结果是 NaN,而不是 0。确认结果为 0 后,把偏置指数设为 0。不要忘记符号:即使结果为 0,乘积符号也是两个操作数符号的 XOR。
乘法的精度
整数乘法需要决定返回乘积的低位字还是完整乘积;浮点乘法也有类似问题:精确乘积应舍入到操作数精度,还是高一级精度?标准高级语言中,整数乘法没有可生成“单精度乘单精度得双精度”指令的结构。
浮点情况则不同。很多语言允许把两个单精度变量的乘积赋给双精度变量,数值算法也能利用这种运算。最著名的例子,是用迭代改进法求解线性方程组。