fma, fmaf, fmal

来自cppreference.com
< c‎ | numeric‎ | math
 
 
 
常用数学函数
函数
基本运算
(C99)
fma
(C99)
(C99)
(C99)
(C99)
(C99)(C99)(C99)
指数函数
(C99)
(C99)
(C99)
(C99)
幂函数
(C99)
(C99)
三角及双曲函数
(C99)
(C99)
(C99)
误差及伽马函数
(C99)
(C99)
(C99)
(C99)
临近整数的浮点运算
(C99)(C99)(C99)
(C99)
(C99)(C99)(C99)
浮点数操作函数
(C99)(C99)
(C99)
(C99)
分类
(C99)
(C99)
(C99)
类型
(C99)(C99)
宏常量
FP_FAST_FMAFFP_FAST_FMAFP_FAST_FMAL
(C99)(C99)(C99)
 
定义于头文件 <math.h>
float       fmaf( float x, float y, float z );
(1) (C99 起)
double      fma( double x, double y, double z );
(2) (C99 起)
long double fmal( long double x, long double y, long double z );
(3) (C99 起)
#define FP_FAST_FMA  /* implementation-defined */
(4) (C99 起)
#define FP_FAST_FMAF /* implementation-defined */
(5) (C99 起)
#define FP_FAST_FMAL /* implementation-defined */
(6) (C99 起)
定义于头文件 <tgmath.h>
#define fma( x, y, z )
(7) (C99 起)
1-3) 计算 (x*y) + z ,如同用无限精度,而仅舍入一次到结果类型。
4-6) 若定义宏常量 FP_FAST_FMAFFP_FAST_FMAFP_FAST_FMAL ,则对应函数 fmaffmafmal 分别求值快于(并且精度高于) floatdoublelong double 参数的表达式 x*y+z 。若定义,则这些宏求值为整数 1
7) 泛型宏:若任何参数拥有 long double 类型,则调用 fmal 。否则若任何参数拥有整数类型或 double 类型,则调用 fma 。否则调用fmaf

参数

x, y, z - 浮点值

返回值

若成功,则返回 (x*y) + z 的值,如同计算为无限精度再舍入一次以适合目标类型(或者说是作为单次三元浮点运算计算)。

若出现上溢所致的值域错误,则返回 ±HUGE_VAL±HUGE_VALF±HUGE_VALL

若出现下溢所致的值域错误,则返回(舍入后的)正确结果。

错误处理

报告 math_errhandling 中指定的错误

若实现支持 IEEE 浮点算术( IEC 60559 ),则

  • 若 x 为零而 y 为无穷大或 x 为无穷大而 y 为零,且 z 非 NaN ,则返回 NaN 并引发 FE_INVALID
  • 若 x 为零而 y 为无穷大或 x 为无穷大而 y 为零,且 z 为 NaN ,则返回 NaN 并可能引发 FE_INVALID
  • 若 x*y 为准确的无穷大且 z 为带相反符号的无穷大,则返回 NaN 并引发 FE_INVALID
  • 若 x 或 y 为 NaN ,则返回 NaN
  • 若 z 为 NaN ,且 x*y 不是 0*Inf 或 Inf*0 ,则返回 NaN (而无 FE_INVALID )

注意

此运算经常在硬件中实现为融合乘加 CPU 指令。若硬件支持,则期待定义相应的 FP_FAST_FMA* 宏,但多数实现即使在不定义这些宏时也利用该 CPU 指令。

POSIX 另外指定被指定为返回 FE_INVALID 的情形是定义域错误。

由于其无限的中间精度, fma 是其他正确舍入数学运算,如 sqrt 或甚至除法(在 CPU 不支持的平台上,例如 Itanium )的常用构建块。

同所有浮点表达式,表达式 (x*y) + z 可编译为融合乘加,除非 #pragma STDC FP_CONTRACT 为关闭。

示例

#include <stdio.h>
#include <math.h>
#include <float.h>
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
int main(void)
{
    // 演示 fma 和内建运算符间的区别
    double in = 0.1;
    printf("0.1 double is %.23f (%a)\n", in, in);
    printf("0.1*10 is 1.0000000000000000555112 (0x8.0000000000002p-3),"
           " or 1.0 if rounded to double\n");
    double expr_result = 0.1 * 10 - 1;
    printf("0.1 * 10 - 1 = %g : 1 subtracted after "
           "intermediate rounding to 1.0\n", expr_result);
    double fma_result = fma(0.1, 10, -1);
    printf("fma(0.1, 10, -1) = %g (%a)\n", fma_result, fma_result);
 
    // fma 用于 double-double
    printf("\nin double-double arithmetic, 0.1 * 10 is representable as ");
    double high = 0.1 * 10;
    double low = fma(0.1, 10, -high);
    printf("%g + %g\n\n", high, low);
 
    // 错误处理
    feclearexcept(FE_ALL_EXCEPT);
    printf("fma(+Inf, 10, -Inf) = %f\n", fma(INFINITY, 10, -INFINITY));
    if(fetestexcept(FE_INVALID)) puts("    FE_INVALID raised");
}

可能的输出:

0.1 double is 0.10000000000000000555112 (0x1.999999999999ap-4)
0.1*10 is 1.0000000000000000555112 (0x8.0000000000002p-3), or 1.0 if rounded to double
0.1 * 10 - 1 = 0 : 1 subtracted after intermediate rounding to 1.0
fma(0.1, 10, -1) = 5.55112e-17 (0x1p-54)
 
in double-double arithmetic, 0.1 * 10 is representable as 1 + 5.55112e-17
 
fma(+Inf, 10, -Inf) = -nan
    FE_INVALID raised

引用

  • C11 standard (ISO/IEC 9899:2011):
  • 7.12.13.1 The fma functions (p: 258)
  • 7.25 Type-generic math <tgmath.h> (p: 373-375)
  • F.10.10.1 The fma functions (p: 530)
  • C99 standard (ISO/IEC 9899:1999):
  • 7.12.13.1 The fma functions (p: 239)
  • 7.22 Type-generic math <tgmath.h> (p: 335-337)
  • F.9.10.1 The fma functions (p: 466)

参阅

计算浮点除法运算的带符号余数
(函数)
(C99)(C99)(C99)
计算除法运算的带符号余数,以及商的后三位
(函数)