Spec-Zone.ru › C++

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

Определено в заголовочном файле <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 для float, double, и long double аргументов соответственно. Если определены, эти макросы возвращают целое число 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 * Бесконечность или Бесконечность * 0, то возвращается NaN (без FE_INVALID).

Примечания

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

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

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

Как и для всех выражений с плавающей точкой, выражение x * y + z может быть скомпилировано как умножение-сложение, если #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)), где /* общий тип с плавающей точкой */ – это тип с плавающей точкой с наибольшим рангом преобразования типов с плавающей точкой и наибольшим подрангом преобразования типов с плавающей точкой среди типов 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/w/cpp/numeric/math/fma

Spec-Zone.ru

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