fma, fmaf, fmal
来自cppreference.com
| 定义于头文件 <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) 各自对于float、 double及long double参数,若宏常量
FP_FAST_FMAF、 FP_FAST_FMA或FP_FAST_FMAL得到定义,则对应的函数fmaf、 fma或fmal求值快于(另外精度也高于)表达式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指定x*y非法且z是NaN的情况为定义域错误。
因为其无限中间精度,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) |
计算浮点除法运算的带符号余数 (函数) |
| (C99) (C99) (C99) |
计算除法运算的带符号余数,以及商的后三位 (函数) |
| fma的 C++ 文档
|

