fma, fmaf, fmal
|
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
定义于头文件
<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 /* 由实现定义 */
|
(4) | (C99 起) |
|
#define FP_FAST_FMAF /* 由实现定义 */
|
(5) | (C99 起) |
|
#define FP_FAST_FMAL /* 由实现定义 */
|
(6) | (C99 起) |
|
定义于头文件
<tgmath.h>
|
||
|
#define fma( x, y, z )
|
(7) | (C99 起) |
FP_FAST_FMA
、
FP_FAST_FMAF
或
FP_FAST_FMAL
,则对应的函数
fma
、
fmaf
或
fmal
分别针对
double
、
float
和
long
double
类型参数,其求值速度(在更高精度的基础上)将快于表达式
x
*
y
+
z
。若这些宏被定义,其求值结果为整型
1
。
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 ,
- 若 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 <fenv.h> #include <float.h> #include <math.h> #include <stdio.h> // #pragma STDC FENV_ACCESS ON int main(void) { // 演示 fma 与内置运算符的区别 double in = 0.1; printf("0.1 的 double 表示为 %.23f (%a)\n", in, in); printf("0.1*10 的结果为 1.0000000000000000555112 (0x8.0000000000002p-3)," " 若四舍五入为 double 类型则显示为 1.0\n"); double expr_result = 0.1 * 10 - 1; printf("0.1 * 10 - 1 = %g : 中间结果四舍五入到 1.0 后减去 1\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 在双精度算术中的应用 printf("\n在双精度算术中,0.1 * 10 可表示为 "); 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 已触发"); }
可能的输出:
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
参考文献
- C23 标准 (ISO/IEC 9899:2024):
-
- 7.12.13.1 fma 函数 (p: TBD)
-
- 7.25 泛型数学 <tgmath.h> (p: TBD)
-
- F.10.10.1 fma 函数 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018):
-
- 7.12.13.1 fma 函数 (p: 188-189)
-
- 7.25 泛型数学 <tgmath.h> (p: 272-273)
-
- F.10.10.1 fma 函数 (p: 386)
- C11 标准 (ISO/IEC 9899:2011):
-
- 7.12.13.1 fma 函数 (p: 258)
-
- 7.25 泛型数学 <tgmath.h> (p: 373-375)
-
- F.10.10.1 fma 函数 (p: 530)
- C99标准(ISO/IEC 9899:1999):
-
- 7.12.13.1 fma函数(第239页)
-
- 7.22 泛型数学 <tgmath.h>(第335-337页)
-
- F.9.10.1 fma函数(第466页)
参见
|
(C99)
(C99)
(C99)
|
计算浮点除法运算的有符号余数
(函数) |
|
(C99)
(C99)
(C99)
|
计算有符号余数及除法运算的最后三位
(函数) |
|
C++ 文档
关于
fma
|
|