Namespaces
Variants

std:: fma, std:: fmaf, std:: fmal

From cppreference.net
Common mathematical functions
Nearest integer floating point operations
(C++11)
(C++11)
(C++11) (C++11) (C++11)
Floating point manipulation functions
(C++11) (C++11)
(C++11)
(C++11)
Classification and comparison
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Types
(C++11)
(C++11)
(C++11)
Macro constants
Определено в заголовочном файле <cmath>
(1)
float fma ( float x, float y, float z ) ;

double fma ( double x, double y, double z ) ;

long double fma ( long double x, long double y, long double z ) ;
(начиная с C++11)
(до C++23)
constexpr /* floating-point-type */

fma ( /* floating-point-type */ x,
/* floating-point-type */ y,

/* floating-point-type */ z ) ;
(начиная с C++23)
float fmaf ( float x, float y, float z ) ;
(2) (начиная с C++11)
(constexpr начиная с C++23)
long double fmal ( long double x, long double y, long double z ) ;
(3) (начиная с C++11)
(constexpr начиная с C++23)
#define FP_FAST_FMA  /* implementation-defined */
(4) (начиная с C++11)
#define FP_FAST_FMAF /* implementation-defined */
(5) (начиная с C++11)
#define FP_FAST_FMAL /* implementation-defined */
(6) (начиная с C++11)
Определено в заголовочном файле <cmath>
template < class Arithmetic1, class Arithmetic2, class Arithmetic3 >

/* common-floating-point-type */

fma ( Arithmetic1 x, Arithmetic2 y, Arithmetic3 z ) ;
(A) (начиная с C++11)
(constexpr начиная с C++23)
1-3) Вычисляет x * y + z как если бы с бесконечной точностью, с однократным округлением для соответствия типу результата. Библиотека предоставляет перегрузки std::fma для всех неквалифицированных cv типов с плавающей точкой в качестве типа параметров x , y и z . (начиная с C++23)
4-6) Если определены макроконстанты FP_FAST_FMA , FP_FAST_FMAF или FP_FAST_FMAL , функция std::fma выполняется быстрее (в дополнение к большей точности), чем выражение x * y + z для аргументов типа double , float и long double соответственно. Если определены, эти макросы вычисляются в целочисленное значение 1 .
A) Дополнительные перегрузки предоставляются для всех остальных комбинаций арифметических типов.

Содержание

Параметры

x, y, z - значения с плавающей точкой или целочисленные значения

Возвращаемое значение

В случае успеха возвращает значение x * y + z так, как если бы оно было вычислено с бесконечной точностью и однократно округлено для соответствия типу результата (или, альтернативно, вычислено как одна тернарная операция с плавающей точкой).

Если происходит ошибка диапазона из-за переполнения, ±HUGE_VAL , ±HUGE_VALF , или ±HUGE_VALL возвращается.

Если возникает ошибка диапазона из-за потери значимости (underflow), возвращается корректное значение (после округления).

Обработка ошибок

Ошибки сообщаются, как указано в 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 ).

Примечания

Эта операция обычно реализуется в аппаратном обеспечении как fused multiply-add инструкция процессора. Если она поддерживается аппаратно, ожидается, что соответствующие FP_FAST_FMA ? макросы будут определены, но многие реализации используют инструкцию процессора даже когда макросы не определены.

POSIX ( fma , fmaf , fmal ) дополнительно указывает, что ситуации, в которых требуется возврат FE_INVALID , являются ошибками домена.

Благодаря своей бесконечной промежуточной точности, std::fma является распространённым строительным блоком для других корректно округлённых математических операций, таких как std::sqrt или даже деление (когда оно не предоставляется процессором, например, на Itanium ).

Как и все выражения с плавающей точкой, выражение x * y + z может быть скомпилировано как fused multiply-add, если только #pragma STDC FP_CONTRACT не отключена.

Дополнительные перегрузки не обязаны быть предоставлены в точности как (A) . Они должны быть лишь достаточными для обеспечения того, чтобы для их первого аргумента 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 ) )
    .
(до C++23)

Если 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 .

Если такого типа с плавающей запятой с наибольшим рангом и подрангом не существует, то разрешение перегрузки не приводит к пригодному кандидату из предоставленных перегрузок.

(начиная с C++23)

Пример

#include <cfenv>
#include <cmath>
#include <iomanip>
#include <iostream>
#ifndef __GNUC__
#pragma STDC FENV_ACCESS ON
#endif
int main()
{
    // демонстрация разницы между fma и встроенными операторами
    const double in = 0.1;
    std::cout << "0.1 в double равно " << std::setprecision(23) << in
              << " (" << std::hexfloat << in << std::defaultfloat << ")\n"
              << "0.1*10 равно 1.0000000000000000555112 (0x8.0000000000002p-3), "
              << "или 1.0 при округлении до 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 вычитается после промежуточного округления\n"
              << "fma(0.1, 10, -1) = " << std::setprecision(6) << fma_result << " ("
              << std::hexfloat << fma_result << std::defaultfloat << ")\n\n";
    // fma используется в double-double арифметике
    const double high = 0.1 * 10;
    const double low = std::fma(0.1, 10, -high);
    std::cout << "в double-double арифметике 0.1 * 10 представляется как "
              << high << " + " << low << "\n\n";
    // обработка ошибок
    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 поднят\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

Смотрите также

(C++11) (C++11) (C++11)
знаковый остаток от операции деления
(функция)
(C++11) (C++11) (C++11)
знаковый остаток, а также три последних бита операции деления
(функция)