fused-multiply-add 指令的重要方面是(实际上)中间结果的无限精度。这有助于提高性能,但不是因为两个操作被编码在一条指令中 - 它有助于提高性能,因为中间结果的几乎无限精度有时很重要,而且用普通的方法恢复非常昂贵当这种精度级别确实是程序员所追求的时,乘法和加法。
示例:比较 a * b 和 1.0
假设对于算法而言,确定两个双精度数 a 和 b 的乘积相对于非零常数的位置至关重要(我们将使用 1.0)。数字a 和b 都具有二进制数字的完整有效位。如果您将a*b 计算为double,则结果可能是1.0,但这并不能告诉您实际的数学乘积是略低于1.0 并四舍五入为1.0,还是略高于1.0 并向下舍入。如果没有 FMA,您的选择是:
将a*b 计算为四精度数。四精度不在硬件中实现,但有软件仿真库。在四精度中,乘积的数学结果可以精确表示,然后您可以将其与 1.0 进行比较。
在向上舍入模式和向下舍入模式下以双精度计算 a*b。如果两个结果都是 1.0,则意味着 a*b 正好是 1.0。如果 RU(a * b) 大于 1.0,则表示数学乘积高于 1.0,如果 RD(a * b) 低于 1.0,则表示数学乘积低于 1.0。在大多数处理器上,这种方法意味着将舍入模式更改 3 次,并且每次更改都代价高昂(涉及刷新 CPU 管道)。
使用 FMA 指令,可以计算 fma(a, b, -1.0) 并将结果与 0.0 进行比较。由于浮点数在零附近更密集,并且由于计算中没有四舍五入中间乘积,我们可以确定fma(a, b, -1.0) > 0 表示a 和b 的数学乘积大于1,以此类推.
示例:Veltkamp/Dekker 乘法
double-double 格式将数字有效地表示为两个双精度浮点数之和。它几乎与四精度一样精确,但利用了现有的双精度硬件。
考虑以下函数Mul12(a, b),它接受两个双精度数a 和b,并将它们的乘积计算为双双数。 Veltkamp 和 Dekker 的算法仅使用双精度加法和乘法来计算此函数 (reference)。它需要 6 次乘法(每个 Split() 的一部分加上算法主体中的 4 次),以及大量的加法。
如果 FMA 指令可用,Mul12 可以实现为两个操作,一个乘法和一个 FMA。
high = a * b; /* double-precision approximation of the real product */
low = fma(a, b, -high); /* remainder of the real product */
/* now the real product of a and b is available as the sum of high and low */
更多示例
FMA 用于计算精度的示例,不仅作为执行乘法和加法的指令,还包括计算平方根和除法。这些运算必须根据 IEEE 754 标准正确舍入(到数学结果的最接近的浮点数)。当有硬件 FMA 指令可用时,这两个操作可以有效地实现。这方面通常被编译链隐藏,但 IA-64 指令集 (Itanium) 没有除法指令。相反,可以通过涉及 FMA 的一系列指令(通常由编译器生成)来获得正确的舍入除法。