Fma
定义于头文件 <cmath>
中。
描述
计算 x * y + z
,就像以无限精度计算并仅舍入一次以适应结果类型。该库为所有 cv-unqualified 浮点类型提供了 std::fma
的重载,作为参数 x
、y
和 z
的类型。
如果定义了宏常量 FP_FAST_FMA
、FP_FAST_FMAF
或 FP_FAST_FMAL
,则函数 std::fma
的评估速度(除了更精确)比 float
、double
和 long double
参数的表达式 x * y + z
分别更快。如果定义了这些宏,它们的值为整数 1
。
声明
- C++23
- C++11
// 1)
constexpr /* floating-point-type */
fma ( /* floating-point-type */ x,
/* floating-point-type */ y,
/* floating-point-type */ z );
// 2)
constexpr float fmaf( float x, float y, float z );
// 3)
constexpr long double fmal( long double x, long double y, long double z );
// 4)
template< class Arithmetic1, class Arithmetic2, class Arithmetic3 >
constexpr /* common-floating-point-type */
fma( Arithmetic1 x, Arithmetic2 y, Arithmetic3 z );
#define FP_FAST_FMA /* implementation-defined */
#define FP_FAST_FMAF /* implementation-defined */
#define FP_FAST_FMAL /* implementation-defined */
// 1)
float fma ( float x, float y, float z );
// 2)
double fma ( double x, double y, double z );
// 3)
long double fma ( long double x, long double y, long double z );
// 4)
float fmaf( float x, float y, float z );
// 5)
long double fmal( long double x, long double y, long double z );
// 6)
template< class Arithmetic1, class Arithmetic2, class Arithmetic3 >
/* common-floating-point-type */
fma( Arithmetic1 x, Arithmetic2 y, Arithmetic3 z );
#define FP_FAST_FMA /* implementation-defined */
#define FP_FAST_FMAF /* implementation-defined */
#define FP_FAST_FMAL /* implementation-defined */
参数
x
, y
, z
- 浮点或整数值
返回值
如果成功,返回 x * y + z
的值,就像以无限精度计算并一次舍入以适应结果类型(或者,作为单个三元浮点运算计算)
如果由于溢出而发生范围错误,则返回 ±HUGE_VAL
、±HUGE_VALF
或 ±HUGE_VALL
。
如果因下溢导致范围错误,则返回正确的值(四舍五入后)。
错误处理
如果实现支持 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 (fma, fmaf, fmal)
另外指定,指定返回 FE_INVALID
的情况是域错误。
由于其无限的中间精度,std::fma
是其他正确舍入的数学操作的常见构建块,例如 std::sqrt
甚至除法(如果 CPU 未提供,例如 Itanium)。
与所有浮点表达式一样,表达式 x * y + z
可以编译为融合乘加,除非 #pragma STDC FP_CONTRACT
关闭。
不需要完全按照“附加重载”提供附加重载。它们只需要足以确保对于它们的第一个参数 num1
、第二个参数 num2
和第三个参数 num3
如果 num1
、num2
或 num3
的类型是 long double
,则
std::fma(num1, num2, num3)
具有与以下相同的效果:std::fma(static_cast<long double>(num1), static_cast<long double>(num2), static_cast<long double>(num3))
。
否则,如果 num1
、num2
和/或 num3
的类型是 double
或整数类型,则
std::fma(num1, num2, num3)
具有与以下相同的效果:std::fma(static_cast<double>(num1), static_cast<double>(num2), static_cast<double>(num3))
。
否则,如果 num1
、num2
或 num3
的类型是 float
,则
std::fma(num1, num2, num3)
具有与以下相同的效果:std::fma(static_cast<float>(num1), static_cast<float>(num2), static_cast<float>(num3))
。
如果 num1
、num2
和 num3
具有算术类型,则
std::fma(num1, num2, num3)
具有与以下相同的效果:
std::fma(static_cast</* common-floating-point-type */>(num1), static_cast</* common-floating-point-type */>(num2), static_cast</* common-floating-point-type */>(num3))
其中 /* common-floating-point-type */ 是在 num1
、num2
和 num3
的类型中具有最高浮点转换等级和最高浮点转换子等级的浮点类型,整数类型的参数被认为与 double
具有相同的浮点转换等级。
如果不存在具有最高等级和子等级的浮点类型,则重载决议不会从提供的重载中产生可用的候选。
示例
#include <cfenv>
#include <cmath>
#include <iomanip>
#include <iostream>
#ifndef __GNUC__
#pragma STDC FENV_ACCESS ON
#endif
int main()
{
// demo the difference between fma and built-in operators
const double in = 0.1;
std::cout
<< "0.1 double is "
<< std::setprecision(23) << in
<< " (" << std::hexfloat << in
<< std::defaultfloat << ")\n"
<< "0.1*10 is 1.0000000000000000555112 (0x8.0000000000002p-3), "
<< "or 1.0 if rounded to double\n";
const double expr_result = 0.1 * 10 - 1;
const double fma_result = std::fma(0.1, 10, -1);
std::cout
<< "0.1 * 10 - 1 = "
<< expr_result
<< " : 1 subtracted after intermediate rounding\n"
<< "fma(0.1, 10, -1) = "
<< std::setprecision(6)
<< fma_result << " ("
<< std::hexfloat
<< fma_result
<< std::defaultfloat << ")\n\n";
// fma is used in double-double arithmetic
const double high = 0.1 * 10;
const double low = std::fma(0.1, 10, -high);
std::cout
<< "in double-double arithmetic, 0.1 * 10 is representable as "
<< high << " + " << low << "\n\n";
// error handling
std::feclearexcept(FE_ALL_EXCEPT);
std::cout
<< "fma(+Inf, 10, -Inf) = "
<< std::fma(INFINITY, 10, -INFINITY)
<< '\n';
if (std::fetestexcept(FE_INVALID))
std::cout
<< "FE_INVALID raised\n";
}
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
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