如何实现fma()

the*_*ine 5 floating-point instruction-set ieee-754 fma

根据文档,有一个fma()功能math.h.这非常好,我知道FMA如何工作以及如何使用它.但是,我不太确定这在实践中如何实施?我最感兴趣的是x86x86_64架构.

是否存在FMA的浮点(非向量)指令,可能是IEEE-754 2008定义的?

是使用FMA3还是FMA4指令?

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

Ste*_*non 7

实际实施因平台而异,但讲得非常广泛:

  • 如果您告诉编译器使用硬件FMA指令(PowerPC,带有VFPv4或AArch64,Intel Haswell或AMD Bulldozer及其后的ARM)定位计算机,编译器可以fma( )通过将适当的指令放入代码中来替换调用.这不是保证,但通常是良好的做法.否则,您将调用数学库,并且:

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

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

  • fma( )函数的结果应始终正确舍入(即"真正的外汇").如果不是,那就是系统数学库中的错误.不幸的是,fma( )正确实现的数学库函数是一个比较困难的,所以很多实现都有bug.请将它们报告给您的图书馆供应商,以便修复它们!

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

鉴于良好的编译器,这不应该是必要的; 它应该足以使用该fma( )函数并告诉编译器您要定位的架构.但是,编译器并不完美,因此您可能需要_mm_fmadd_sd( )在x86上使用相关的内在函数(但是将错误报告给编译器供应商!)

  • "有机会解释圆形到奇数就像环法自行车赛:一个人等了很长时间才快速通过." (2认同)

Z b*_*son 6

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

typedef struct { float hi; float lo; } doublefloat;  
doublefloat split(float a) {
    float t = ((1<<12)+1)*a;
    float hi = t - (t - a);
    float lo = a - hi;
    return (doublefloat){hi, lo};
}
Run Code Online (Sandbox Code Playgroud)

拆分浮点数后,您可以a*b-c使用这样的单个舍入进行计算

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;
}
Run Code Online (Sandbox Code Playgroud)

这基本上c(ahi,alo)*(bhi,blo) = (ahi*bhi + ahi*blo + alo*bhi + alo*blo).

我从twoProd论文Extended-Precision Floating-Point Numbers for GPU Computation 中mul_sub_x函数和Agner Fog 的向量类库中的函数中得到了这个想法。他使用不同的函数来分割不同分割的浮点数向量。我试图在这里重现一个标量版本

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};
}
Run Code Online (Sandbox Code Playgroud)

在使用任何情况下splitsplit2fmsub同意很好地fma(a,b,-c)从glibc中的数学库。无论出于何种原因,我的版本都比fma具有硬件 fma 的机器快得多(在这种情况下我_mm_fmsub_ss无论如何都使用)。


小智 5

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

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

正确的实现需要使用高于双精度的求和算法,或者以数量级递减的顺序添加项。

  • 是的,不,你是对的。如果 `c` 非常小,那么当从 `ahi*bhi` 中减去时它会被四舍五入淹没,它根本没有帮助。他需要形成一个更长的展开式,并使用所谓的 Kahan 求和从最小的元素开始添加。即使结果四舍五入为浮动,这种排序仍然很重要,因为它会影响舍入方向。 (2认同)