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, соответствующая функция fmaf, fma, или fmal выполняется быстрее (кроме большей точности), чем выражение x*y+z для аргументов float, double, и long double соответственно. Если определены, эти макросы вычисляются как целые числа 1.
7) Тип-обобщённый макрос: Если какой-либо аргумент имеет тип long double, вызывается 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
  • Если x равно нулю, а y — бесконечности, или если x — бесконечность, а y — ноль, и 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 <stdio.h>
#include <math.h>
#include <float.h>
#include <fenv.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

Справочная информация

  • Стандарт 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/w/c/numeric/math/fma

Spec-Zone.ru

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