FMA(融合乘加)指令是否总是产生与 mul then add 指令相同的结果?
Do FMA (fused multiply-add) instructions always produce the same result as a mul then add instruction?
我有这个程序集(AT&T 语法):
mulsd %xmm0, %xmm1
addsd %xmm1, %xmm2
我想将其替换为:
vfmadd231sd %xmm0, %xmm1, %xmm2
此转换是否会始终在所有涉及的寄存器和标志中留下等效状态?还是结果浮动会在某种程度上略有不同? (如果它们不同,那是为什么?)
没有。事实上,融合乘加的一个主要好处是它不会(必然)产生与单独的乘加相同的结果。
举个(有点做作的)例子,假设我们有:
double a = 1 + 0x1.0p-52 // 1 + 2**-52
double b = 1 - 0x1.0p-52 // 1 - 2**-52
我们要计算 a*b - 1
。 a*b - 1
的"mathematically exact"值为:
(1 + 2**-52)(1 - 2**-52) - 1 = 1 + 2**-52 - 2**52 - 2**-104 - 1 = -2**-104
但如果我们首先使用乘法计算 a*b
,它会四舍五入为 1.0,因此随后减去 1.0 会产生零结果。
如果我们使用 fma(a,b,-1)
代替,我们消除了乘积的中间舍入,这使我们能够得到 "real" 答案,-1.0p-104
。
请注意,我们不仅得到了不同的结果,而且还设置了不同的标志;单独的乘法和减法设置不精确标志,而融合乘加不设置任何标志。
我有这个程序集(AT&T 语法):
mulsd %xmm0, %xmm1
addsd %xmm1, %xmm2
我想将其替换为:
vfmadd231sd %xmm0, %xmm1, %xmm2
此转换是否会始终在所有涉及的寄存器和标志中留下等效状态?还是结果浮动会在某种程度上略有不同? (如果它们不同,那是为什么?)
没有。事实上,融合乘加的一个主要好处是它不会(必然)产生与单独的乘加相同的结果。
举个(有点做作的)例子,假设我们有:
double a = 1 + 0x1.0p-52 // 1 + 2**-52
double b = 1 - 0x1.0p-52 // 1 - 2**-52
我们要计算 a*b - 1
。 a*b - 1
的"mathematically exact"值为:
(1 + 2**-52)(1 - 2**-52) - 1 = 1 + 2**-52 - 2**52 - 2**-104 - 1 = -2**-104
但如果我们首先使用乘法计算 a*b
,它会四舍五入为 1.0,因此随后减去 1.0 会产生零结果。
如果我们使用 fma(a,b,-1)
代替,我们消除了乘积的中间舍入,这使我们能够得到 "real" 答案,-1.0p-104
。
请注意,我们不仅得到了不同的结果,而且还设置了不同的标志;单独的乘法和减法设置不精确标志,而融合乘加不设置任何标志。