C 融合乘加和默认舍入模式

C 融合乘加和默认舍入模式,c,gcc,clang,ieee-754,fma,C,Gcc,Clang,Ieee 754,Fma,对于GCC 5.3,以下代码与-O3-fma float mul_add(float a, float b, float c) { return a*b + c; } 生成以下程序集 vfmadd132ss %xmm1, %xmm2, %xmm0 ret 带有-O3-mfma的Clang 3.7产生 vmulss %xmm1, %xmm0, %xmm0 vaddss %xmm2, %xmm0, %xmm0 retq 但是使用-Ofast-mfma的clang3.7与使用-

对于GCC 5.3,以下代码与
-O3-fma

float mul_add(float a, float b, float c) {
  return a*b + c;
}
生成以下程序集

vfmadd132ss     %xmm1, %xmm2, %xmm0
ret

带有
-O3-mfma的Clang 3.7产生

vmulss  %xmm1, %xmm0, %xmm0
vaddss  %xmm2, %xmm0, %xmm0
retq
但是使用
-Ofast-mfma
的clang3.7与使用
-O3 fast
的GCC产生相同的代码

我很惊讶GCC使用了
-O3
,因为它说

编译器不允许融合分离的加法和乘法,除非您允许宽松的浮点模型

这是因为FMA只有一个舍入,而ADD+MUL有两个舍入。因此,编译器将通过融合违反严格的IEEE浮点行为

然而,从它说

不管FLT_EVAL_方法的值是多少,任何浮点表达式都可以收缩,也就是说,计算时就好像所有中间结果的范围和精度都是无限的一样

所以现在我感到困惑和担心

  • GCC是否有理由将FMA与
    -O3
    一起使用

  • 融合是否违反了严格的IEEE浮点行为
  • 如果融合确实违反了IEEE浮点逻辑,那么这不是矛盾吗
  • 由于FMA,FMA似乎应该有两个编译器开关:一个告诉编译器在计算中使用FMA,另一个告诉编译器硬件有FMA


    显然,这可以通过选项
    -ffp contract
    进行控制。对于GCC,默认值是
    -ffp contract=fast
    ,而对于Clang则不是。其他选项,如
    -ffp contract=on
    -ffp contract=off
    不会生成FMA指令

    例如,带有
    -O3-mfma-ffp contract=fast的clang3.7产生
    vfmad132ss


    我检查了
    #pragma STDC FP_CONTRACT
    设置为
    的一些排列,其中
    -ffp CONTRACT
    设置为
    。在所有情况下,我也使用了
    -O3-mfma

    有了GCC,答案很简单<代码>#pragma STDC FP_合同
    开启或关闭没有区别。只有
    -ffp合同
    才重要

    GCC使用
    fma

  • -ffp contract=fast
    (默认值)
  • 它使用叮当声
    fma

    float mul_add(float a, float b, float c) {
      return a*b + c;
    }
    
  • 使用
    -ffp合同=fast
  • -ffp contract=on
    (默认)和
    #pragma STDC FP_contract on
    (默认为
    关闭
  • 换句话说,使用Clang,您可以使用
    #pragma STDC FP_CONTRACT ON
    (因为
    -ffp CONTRACT=ON
    是默认值)或
    -ffp CONTRACT=fast
    获得
    fma
    -ffast math
    (因此
    -Ofast
    )set
    -ffp contract=fast


    我调查了MSVC和ICC

    对于MSVC,它使用带有
    /O2/arch:AVX2/fp:fast
    的fma指令。对于MSVC
    /fp:precise
    是默认值

    对于ICC,它使用带有
    -O3-march=core-avx2
    (通常
    -O1
    就足够了)的fma。这是因为默认情况下,ICC使用
    -fp model fast
    。但ICC使用fma,即使是
    -fp模型精确的
    。要使用ICC禁用fma,请使用
    -fp model strict
    -no fma


    因此,默认情况下,当启用fma时,GCC和ICC使用fma(对于GCC/Clang,使用
    -mfma
    ,或者对于ICC,使用
    -march=core-avx2
    ),但Clang和MSVC不使用fma。

    当您引用允许使用融合乘法加法时,您省略了重要条件“除非pragma FP_合同关闭”。这是C语言中的一个新特性(我认为是在C99中引入的),PowerPC绝对需要它,它从一开始就融合了乘法加法-实际上,x*y相当于fma(x,y,0),x+y相当于fma(1.0,x,y)

    FP_合同是控制融合乘法/加法的,而不是FLT_评估方法。虽然如果FLT_EVAL_方法允许更高的精度,那么合同始终是合法的;只需假设操作是以非常高的精度执行的,然后四舍五入


    如果您不想要速度,但想要精度,fma功能非常有用。它将缓慢但正确地计算收缩结果,即使它在硬件中不可用。如果它在硬件中可用,则应该是内联的

    这并不违反IEEE-754,因为IEEE-754在这一点上遵从语言:

    语言标准还应定义并要求实现提供允许和不允许对块单独或集体进行值更改优化的属性。这些优化可能包括但不限于:

    ―从乘法和加法合成fusedMultiplyAdd运算


    在标准C中,
    STDC FP_契约
    pragma提供了控制该值变化优化的方法。因此,默认情况下,GCC被授权执行融合,只要它允许您通过设置
    STDC FP_CONTRACT OFF
    来禁用优化。不支持这一点意味着不遵守C标准。

    可能是一个编译器错误。考虑报告吧。我很确定海湾合作委员会正在做什么。在阅读了关于收缩FP表达式的FLT_EVAL_方法文档之后,我很惊讶
    clang
    没有这样做。我不是在回答这个问题,因为它不是基于任何真正的标准文档,只是根据我的理解,考虑到问题中的内容,我认为事情应该如何工作/应该如何设计。@fuzzxl,你认为浮点标记比ieee-754更合适吗?(如果有,请随意更改)。我觉得我也应该使用浮点标记。“融合是否违反了严格的IEEE浮点行为?”-->IMO,是的。使用
    双fma(双x、双y、双z)相反,这是一个函数调用,在优化的编译器中,它将调用预期的汇编代码。这并不违反法律