Floating point fma()是如何实现的

Floating point fma()是如何实现的,floating-point,ieee-754,instruction-set,fma,Floating Point,Ieee 754,Instruction Set,Fma,根据,在math.h中有一个fma()函数。这很好,我知道FMA是如何工作的,以及它的用途。然而,我不太确定这在实践中是如何实现的?我最感兴趣的是x86和x86\u 64体系结构 FMA是否有浮点(非矢量)指令,可能如IEEE-754 2008所定义 是否使用FMA3或FMA4指令 在依赖精度的情况下,是否存在确保使用真正的FMA的内在因素?实际实施因平台而异,但从广义上讲: 如果您告诉编译器以具有硬件FMA指令的机器为目标(PowerPC、带VFPv4或AArch64的ARM、Intel H

根据,在
math.h
中有一个
fma()
函数。这很好,我知道FMA是如何工作的,以及它的用途。然而,我不太确定这在实践中是如何实现的?我最感兴趣的是
x86
x86\u 64
体系结构

FMA是否有浮点(非矢量)指令,可能如IEEE-754 2008所定义

是否使用FMA3或FMA4指令


在依赖精度的情况下,是否存在确保使用真正的FMA的内在因素?

实际实施因平台而异,但从广义上讲:

  • 如果您告诉编译器以具有硬件FMA指令的机器为目标(PowerPC、带VFPv4或AArch64的ARM、Intel Haswell或AMD推土机及更高版本),编译器可能会通过将适当的指令放入代码中来替换对
    FMA()
    的调用。这不是保证,但通常是良好的做法。否则,将调用数学库,并且:

  • 在具有硬件FMA的处理器上运行时,应使用这些指令来实现该功能。但是,如果您有较旧版本的操作系统或较旧版本的数学库,它可能不会利用这些指令

  • 如果您在没有硬件FMA的处理器上运行,或者您使用的是旧的(或不是很好的)数学库,那么将使用FMA的软件实现。这可以使用巧妙的扩展精度浮点技巧或整数算术来实现

  • fma()
    函数的结果应始终正确四舍五入(即“真正的fma”)。如果不是,那就是系统数学库中的错误。不幸的是,
    fma()
    是更难正确实现的数学库函数之一,因此许多实现都有bug。请向您的图书馆供应商报告,以便修复

在依赖精度的情况下,是否存在确保使用真实FMA的内在因素


如果有一个好的编译器,这应该是不必要的;使用
fma()
函数并告诉编译器您的目标是什么体系结构就足够了。但是,编译器并不完美,因此您可能需要在x86上使用
\u mm\u fmadd\u sd()
和相关的内部函数(但请向编译器供应商报告错误!)

在软件中实现FMA的一种方法是将有效值拆分为高位和低位。我用

这基本上是从
(ahi,alo)*(bhi,blo)=(ahi*bhi+ahi*blo+alo*bhi+alo*blo)
中减去
c

我从论文中的
twoProd
函数和中的
mul\u sub\u x
函数中得到了这个想法。他使用一个不同的函数来分割不同分割的浮点向量。我试图在这里复制一个标量版本

typedef union {float f; int i;} u;
doublefloat split2(float a) {
    u lo, hi = {a};
    hi.i &= -(1<<12);
    lo.f = a - hi.f;
    return (doublefloat){hi.f,lo.f};
}
typedef并集{float f;int i;}u;
双浮点数拆分2(浮点数a){
u-lo,hi={a};

hi.i&=-(1Z玻色子基于Dekker算法的FMA建议不幸不正确。与Dekker的Two-product不同,在更一般的FMA情况下,c的大小相对于乘积项是未知的,因此可能发生错误的抵消

因此,虽然Dekker的twoProduct可以通过硬件FMA大大加速,但Dekker的twoProduct的误差项计算并不是一个健壮的FMA实现


正确的实现需要使用高于两倍精度的求和算法,或者按数量级递减的顺序添加术语。

“一个向奇数解释回合的机会就像环法自行车赛:人们等待很长时间,然后很快就过去了。”@如果我没有弄错的话,PascalCuoq IEEE-754默认使用从圆到偶数。为什么从圆到奇数与此相关?我目前正在实现一个多精度库,所以我对内部工作有点熟悉,但我没有听说从圆到奇数特别重要。非常有诗意的顺便说一句,做得好!@thesween如果你有格式的话使用目标FMA宽度的两倍,您可以无误地执行乘法。假设您正在以双精度
double
实现
fmaf
。您将面临添加
double
(double)a*(double)的问题b
和a
浮点c
并将此加法四舍五入到最近的
浮点
。此操作通常不可用,但可以实现为将
加法四舍五入到奇数,然后从
四舍五入到
浮点
四舍五入到最近的整数。中间结果不使用四舍五入到奇数导致双舍入问题。@请在代码中查看:我没有编写链接到的修补程序,但我确实使用天真的方法编写了正确的(AFAICT)
fmaf
(假设a、b、c≥ 0)。如果您对这个主题感兴趣,还应该看看这个实现:在x86和x86_64上,如果被告知允许,gcc会发出fma指令(
-mfma
-mfma4
-march=something
其中
something
是fma支持处理器)。在Linux上,您可以查看glibc中的
sysdeps/ieee754/dbl-64/s_fma.c
,了解库函数回退的样子。参考文献不错。我知道Schewchuk和Prist的工作。在这个问题上,我更感兴趣的是当前指令集中有哪些指令。我猜
\u mm_fmadd\u ss
概括起来,你的版本可能会更快,因为它不处理特殊的数字(特别是无穷大)。我可能错了,但用无穷大进行乘法/加法似乎会导致Dekker的算法生成NaN。我希望运行时在那里能够正确运行,因此速度会受到影响。这远远不止是
\u mm\f
float fmsub(float a, float b, float c) {
    doublefloat as = split(a), bs = split(b);
    return ((as.hi*bs.hi - c) + as.hi*bs.lo + as.lo*bs.hi) + as.lo*bs.lo;
}
typedef union {float f; int i;} u;
doublefloat split2(float a) {
    u lo, hi = {a};
    hi.i &= -(1<<12);
    lo.f = a - hi.f;
    return (doublefloat){hi.f,lo.f};
}