Namensräume
Varianten
Aktionen

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

Von cppreference.com
< cpp‎ | numeric‎ | math
 
 
 
 
Definiert in der Header-Datei <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 );
(seit C++11)
(bis C++23)
constexpr /* gleitkommazahl-Typ */

            fma ( /* Gleitkommatyp */ x,
                  /* Gleitkommatyp */ y,

                  /* Gleitkommatyp */ z );
(seit C++23)
float       fmaf( float x, float y, float z );
(2) (seit C++11)
(constexpr seit C++23)
long double fmal( long double x, long double y, long double z );
(3) (seit C++11)
(constexpr seit C++23)
#define FP_FAST_FMA  /* implementierungsabhängig */
(4) (seit C++11)
#define FP_FAST_FMAF /* implementierungsabhängig */
(5) (seit C++11)
#define FP_FAST_FMAL /* implementierungsabhängig */
(6) (seit C++11)
Definiert in der Header-Datei <cmath>
template< class Arithmetic1, class Arithmetic2, class Arithmetic3 >

/* common-floating-point-type */

    fma( Arithmetic1 x, Arithmetic2 y, Arithmetic3 z );
(A) (seit C++11)
(constexpr seit C++23)
1-3) Berechnet x * y + z, als ob es mit unendlicher Präzision berechnet und nur einmal gerundet würde, um dem Ergebnistyp zu entsprechen. Die Bibliothek stellt Überladungen von std::fma für alle cv-unqualifizierten Gleitkommatypen als Typ der Parameter x, y und z bereit.(seit C++23)
4-6) Wenn die Makrokonstanten FP_FAST_FMA, FP_FAST_FMAF oder FP_FAST_FMAL definiert sind, evaluiert die Funktion std::fma schneller (zusätzlich zur höheren Präzision) als der Ausdruck x * y + z für Argumente vom Typ double, float bzw. long double. Wenn sie definiert sind, werten diese Makros zu einer Ganzzahl 1 aus.
A) Zusätzliche Überladungen werden für alle anderen Kombinationen von arithmetischen Typen bereitgestellt.

Inhalt

[edit] Parameter

x, y, z - Gleitkomma- oder Ganzzahlwerte

[edit] Rückgabewert

Bei Erfolg gibt den Wert von x * y + z zurück, als ob er mit unendlicher Präzision berechnet und einmal gerundet worden wäre, um dem Ergebnistyp zu entsprechen (oder alternativ als einzelne ternäre Gleitkommaoperation berechnet).

Wenn ein Bereichsfehler aufgrund von Überlauf auftritt, wird ±HUGE_VAL, ±HUGE_VALF oder ±HUGE_VALL zurückgegeben.

Wenn ein Bereichsfehler aufgrund von Unterlauf auftritt, wird der korrekte Wert (nach Rundung) zurückgegeben.

[edit] Fehlerbehandlung

Fehler werden wie in math_errhandling beschrieben gemeldet.

Wenn die Implementierung IEEE-Gleitkomma-Arithmetik (IEC 60559) unterstützt,

  • Wenn x Null ist und y unendlich ist oder wenn x unendlich ist und y Null ist, und
    • wenn z kein NaN ist, wird NaN zurückgegeben und FE_INVALID ausgelöst,
    • wenn z ein NaN ist, wird NaN zurückgegeben und FE_INVALID kann ausgelöst werden.
  • Wenn x * y eine exakte Unendlichkeit ist und z eine Unendlichkeit mit entgegengesetztem Vorzeichen ist, wird NaN zurückgegeben und FE_INVALID ausgelöst.
  • Wenn x oder y NaN sind, wird NaN zurückgegeben.
  • Wenn z ein NaN ist und x * y nicht 0 * Inf oder Inf * 0 ist, wird NaN zurückgegeben (ohne FE_INVALID).

[edit] Anmerkungen

Diese Operation wird üblicherweise in Hardware als Fused Multiply-Add-CPU-Instruktion implementiert. Wenn von der Hardware unterstützt, wird erwartet, dass die entsprechenden Makros FP_FAST_FMA? definiert sind, aber viele Implementierungen nutzen die CPU-Instruktion auch dann, wenn die Makros nicht definiert sind.

POSIX (fma, fmaf, fmal) spezifiziert zusätzlich, dass die Situationen, in denen FE_INVALID zurückgegeben wird, Domänenfehler sind.

Aufgrund seiner unendlichen Zwischenpräzision ist std::fma ein gängiger Baustein für andere korrekt gerundete mathematische Operationen, wie z. B. std::sqrt oder sogar Division (wenn nicht von der CPU bereitgestellt, z. B. Itanium).

Wie bei allen Gleitkommaausdrücken kann der Ausdruck x * y + z als Fused Multiply-Add kompiliert werden, es sei denn, das #pragma STDC FP_CONTRACT ist ausgeschaltet.

Die zusätzlichen Überladungen müssen nicht exakt wie in (A) angegeben bereitgestellt werden. Sie müssen nur ausreichen, um sicherzustellen, dass für ihr erstes Argument num1, zweites Argument num2 und drittes Argument num3

  • Wenn num1, num2 oder num3 vom Typ long double ist, hat std::fma(num1, num2, num3) denselben Effekt wie std::fma(static_cast<long double>(num1),
             static_cast<long double>(num2),
             static_cast<long double>(num3))
    .
  • Andernfalls, wenn num1, num2 und/oder num3 vom Typ double oder einem ganzzahligen Typ sind, hat std::fma(num1, num2, num3) denselben Effekt wie std::fma(static_cast<double>(num1),
             static_cast<double>(num2),
             static_cast<double>(num3))
    .
  • Andernfalls, wenn num1, num2 oder num3 vom Typ float ist, hat std::fma(num1, num2, num3) denselben Effekt wie std::fma(static_cast<float>(num1),
             static_cast<float>(num2),
             static_cast<float>(num3))
    .
(bis C++23)

Wenn num1, num2 und num3 arithmetische Typen haben, hat std::fma(num1, num2, num3) denselben Effekt wie std::fma(static_cast</*gemeinsamer Gleitkommatyp*/>(num1),
         static_cast</*gemeinsamer Gleitkommatyp*/>(num2),
         static_cast</*gemeinsamer Gleitkommatyp*/>(num3))
, wobei /*gemeinsamer Gleitkommatyp*/ der Gleitkommatyp mit dem höchsten Rang der Gleitkommakonvertierung und dem höchsten Unterrang der Gleitkommakonvertierung unter den Typen von num1, num2 und num3 ist. Argumente vom ganzzahligen Typ werden so betrachtet, als hätten sie denselben Rang der Gleitkommakonvertierung wie double.

Wenn kein solcher Gleitkommazahltyp mit dem höchsten Rang und Subrang existiert, dann führt die Überladungsauflösung nicht zu einem nutzbaren Kandidaten aus den bereitgestellten Überladungen.

(seit C++23)

[edit] Beispiel

#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";
}

Mögliche Ausgabe

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

[edit] Siehe auch

(C++11)(C++11)(C++11)
Vorzeichenbehafteter Rest der Divisionsoperation
(Funktion) [bearbeiten]
(C++11)(C++11)(C++11)
vorzeichenbehafteter Rest sowie die letzten drei Bits der Divisionsoperation
(Funktion) [bearbeiten]