Spec-Zone.ru › C

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  /* implementation-defined */
(4) (начиная с C99)
#define FP_FAST_FMAF /* implementation-defined */
(5) (начиная с C99)
#define FP_FAST_FMAL /* implementation-defined */
(6) (начиная с C99)
Определено в заголовочном файле <tgmath.h>
#define fma( x, y, z )
(7) (начиная с C99)
1-3) Вычисляет (x * y) + z так, как если бы вычисление выполнялось с бесконечной точностью, а округление производилось только один раз для соответствия типу результата.
4-6) Если определены макроконстанты FP_FAST_FMA, FP_FAST_FMAF или FP_FAST_FMAL, соответствующая функция fma, fmaf или fmal выполняется быстрее (и при этом обеспечивает большую точность), чем выражение x * y + z для аргументов типов double, float и longdouble соответственно. Если эти макросы определены, они принимают целочисленное значение 1.
7) Обобщённый макрос по типу: если хотя бы один аргумент имеет тип longdouble, вызывается 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).

Примечания

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

POSIX устанавливает, что ситуация, когда значение x * y недопустимо, а z является NaN, считается ошибкой области определения.

Благодаря бесконечной точности промежуточных вычислений fma часто используется как строительный блок для других математических операций с корректным округлением, таких как sqrt или даже деление (если оно не поддерживается процессором, например, на 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)
{
    // demo the difference between fma and built-in operators
    double in = 0.1;
    printf("0.1 double is %.23f (%a)\n", in, in);
    printf("0.1*10 is 1.0000000000000000555112 (0x8.0000000000002p-3),"
           " or 1.0 if rounded to double\n");
    double expr_result = 0.1 * 10 - 1;
    printf("0.1 * 10 - 1 = %g : 1 subtracted after "
           "intermediate rounding to 1.0\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 use in double-double arithmetic
    printf("\nin double-double arithmetic, 0.1 * 10 is representable as ");
    double high = 0.1 * 10;
    double low = fma(0.1, 10, -high);
    printf("%g + %g\n\n", high, low);

    // error handling
    feclearexcept(FE_ALL_EXCEPT);
    printf("fma(+Inf, 10, -Inf) = %f\n", fma(INFINITY, 10, -INFINITY));
    if (fetestexcept(FE_INVALID))
        puts("    FE_INVALID raised");
}

Возможный вывод:

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 (стр.: TBD)
    • 7.25 Обобщённая по типу математика <tgmath.h> (стр.: TBD)
    • F.10.10.1 Функции fma (стр.: TBD)
  • Стандарт C17 (ISO/IEC 9899:2018):
    • 7.12.13.1 Функции fma (стр.: 188-189)
    • 7.25 Обобщённая по типу математика <tgmath.h> (стр.: 272-273)
    • F.10.10.1 Функции fma (стр.: 386)
  • Стандарт C11 (ISO/IEC 9899:2011):
    • 7.12.13.1 Функции fma (стр.: 258)
    • 7.25 Обобщённая по типу математика <tgmath.h> (стр.: 373-375)
    • F.10.10.1 Функции fma (стр.: 530)
  • Стандарт C99 (ISO/IEC 9899:1999):
    • 7.12.13.1 Функции fma (стр.: 239)
    • 7.22 Обобщённая по типу математика <tgmath.h> (стр.: 335-337)
    • F.9.10.1 Функции fma (стр.: 466)

См. также

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

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

Spec-Zone.ru

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