Spec-Zone.ru › C++

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

Определено в заголовочном файле <cmath>
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 );
(1) (начиная с 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 и longdouble соответственно. Если они определены, эти макросы имеют целочисленное значение 1.
A) Для всех остальных комбинаций арифметических типов предоставляются дополнительные перегрузки.

Параметры

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).

Примечания

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

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

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

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

Не требуется, чтобы дополнительные перегрузки были предоставлены в точности в виде (A). Они должны лишь обеспечивать, чтобы для первого аргумента num1, второго аргумента num2 и третьего аргумента num3:

  • Если num1, num2 или num3 имеет тип longdouble, то 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()
{
    // 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

См. также

remainderremainderfremainderl
(C++11)(C++11)(C++11)
знаковый остаток от деления
(функция)
remquoremquofremquol
(C++11)(C++11)(C++11)
знаковый остаток и три последних бита результата деления
(функция)
Документация по C для fma

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/numeric/math/fma

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API