C-Standardbibliothek<fenv.h>

C-Standardbibliothek<fenv.h>

<fenv.h>ist eine Header-Datei in der C-Standardbibliothek, die zur Steuerung der Gleitkommaumgebung verwendet wird.

<fenv.h>Im C99-Standard eingeführt, bietet sie Funktionalität zur Steuerung und Abfrage von Gleitkomma-Ausnahmen, Rundungsmodi und anderen Gleitkomma-Zuständen.


<fenv.h>Der Hauptzweck ist:

  • Erkennen und Behandeln von Gleitkomma-Ausnahmen (wie Division durch Null, Überlauf usw.).

  • Steuern des Rundungsmodus für Gleitkommaoperationen (wie Rundung gegen Null, Rundung auf den nächsten Wert usw.).

  • Abfragen und Ändern von Gleitkomma-Statusflags.


1, Gleitkomma-Ausnahmen

Gleitkomma-Ausnahmen sind besondere Situationen, die während Gleitkommaoperationen auftreten, zum Beispiel:

  • FE_DIVBYZERO: Division durch Null.

  • FE_INEXACT: Ungenaues Ergebnis.

  • FE_INVALID: ungültige Operation (wie das Ziehen der Quadratwurzel aus einer negativen Zahl).

  • FE_OVERFLOW: Überlauf.

  • FE_UNDERFLOW: Unterlauf.

Diese Ausnahmen werden durch Gleitkomma-Statusflags repräsentiert und können<fenv.h>Die darin enthaltenen Funktionen erkennen und behandeln.


2, Rundungsmodi

Der Rundungsmodus steuert, wie die Ergebnisse von Gleitkommaoperationen gerundet werden.<fenv.h>Die folgenden Rundungsmodi sind definiert:

  • FE_TONEAREST: auf den nächstgelegenen Wert runden (Standardmodus).

  • FE_DOWNWARD: Richtung negative Unendlichkeit runden.

  • FE_UPWARD: Richtung positive Unendlichkeit runden.

  • FE_TOWARDZERO: Richtung null runden.


3, Hauptfunktionen und Makros

<fenv.h>Stellt eine Reihe von Funktionen und Makros für die Manipulation der Gleitkommaumgebung und die Behandlung von Gleitkommaausnahmen bereit.

Behandlung von Gleitkommaausnahmen

Funktionen/Makros Beschreibung
feclearexcept(int excepts) Die angegebenen Gleitkomma-Ausnahmeflags löschen
feraiseexcept(int excepts) Die angegebene Gleitkomma-Ausnahme auslösen
fetestexcept(int excepts) Testen, ob die angegebene Gleitkomma-Ausnahme aufgetreten ist
fegetexceptflag(fexcept_t *flagp, int excepts) Status des Gleitkomma-Ausnahmeflags abrufen
fesetexceptflag(const fexcept_t *flagp, int excepts) Status des Gleitkomma-Ausnahmeflags setzen

Rundungsmodus-Steuerung

Funktionen/Makros Beschreibung
fegetround(void) Aktuellen Rundungsmodus abrufen
fesetround(int round) Aktuellen Rundungsmodus setzen

Steuerung der Gleitkommaumgebung

Funktionen/Makros Beschreibung
fegetenv(fenv_t *envp) Aktuelle Gleitkommaumgebung speichern
fesetenv(const fenv_t *envp) Wiederherstellen der Gleitkommaumgebung
feholdexcept(fenv_t *envp) Aktuelle Gleitkommaumgebung speichern und Ausnahmeflags löschen.
feupdateenv(const fenv_t *envp) Gleitkommaumgebung wiederherstellen und Ausnahmen auslösen

4, Beispiel

Das Folgende ist ein Beispiel unter Verwendung von<fenv.h>Ein Beispiel hierfür, das zeigt, wie man Gleitkomma-Ausnahmen erkennt und den Rundungsmodus steuert:

Beispiel

#include <stdio.h>
#include <fenv.h>
#include <math.h>

int main() {
    // Erkennung von Gleitkomma-Ausnahmen aktivieren
    feclearexcept(FE_ALL_EXCEPT);

    // Division-durch-Null-Ausnahme auslösen
    double x = 1.0, y = 0.0;
    double z = x / y;

    // Prüfen, ob eine Division-durch-Null-Ausnahme aufgetreten ist
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Division-durch-Null-Ausnahme aufgetreten.\n");
    }

    // Rundungsmodus auf Rundung gegen Null setzen
    fesetround(FE_TOWARDZERO);

    // Rundungsmodus testen
    double a = 1.5;
    double b = rint(a); // Auf Ganzzahl runden
    printf("Rundung von 1.5 gegen null: %.1f\n", b);

    return 0;
}

Das Ausgabeergebnis ist:

Divide by zero exception occurred.
Rounding 1.5 toward zero: 1.0

Erkennen von Gleitkomma-Ausnahmen

Beispiel

#include <fenv.h>
#include <stdio.h>
#include <math.h>

#pragma STDC FENV_ACCESS ON

void check_exceptions() {
    // Alle Ausnahme-Flags löschen
    feclearexcept(FE_ALL_EXCEPT);
   
    // Operationen ausführen, die Ausnahmen auslösen können
    double x = 0.0;
    double y = 1.0 / x;  // Division durch null
   
    // Auf eine bestimmte Ausnahme prüfen
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Division durch null ist aufgetreten!\n");
    }
   
    // Auf eine beliebige Ausnahme prüfen
    if (fetestexcept(FE_ALL_EXCEPT)) {
        printf("Mindestens eine Gleitkomma-Ausnahme ist aufgetreten\n");
    }
}

int main() {
    check_exceptions();
    return 0;
}

Rundungsrichtung steuern

Beispiel

#include <fenv.h>
#include <stdio.h>

#pragma STDC FENV_ACCESS ON

void test_rounding(double num) {
    int modes[] = {FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO};
    const char *names[] = {"Nächste", "Aufwärts", "Abwärts", "Richtung Null"};
   
    for (int i = 0; i < 4; i++) {
        if (fesetround(modes[i]) == 0) {
            printf("Rundung %s: %.1f -> %.0f\n",
                  names[i], num, rint(num));
        }
    }
}

int main() {
    test_rounding(2.5);  // Teste das Ergebnis von 2.5 in verschiedenen Rundungsmodi
    test_rounding(-2.5); // Testen Sie das Ergebnis von -2.5 in verschiedenen Rundungsmodi
    return 0;
}

Gleitkommaumgebung speichern und wiederherstellen

Beispiel

#include <fenv.h>
#include <stdio.h>
#include <math.h>

#pragma STDC FENV_ACCESS ON

void sensitive_calculation() {
    fenv_t env;
   
    // Aktuelle Umgebung speichern
    fegetenv(&env);
   
    // Strikte Umgebung setzen: alle Ausnahmen abfangen
    feclearexcept(FE_ALL_EXCEPT);
   
    // Kritische Berechnung durchführen
    double result = sqrt(-1.0);  // Ungültige Operation
   
    if (fetestexcept(FE_INVALID)) {
        printf("Ungültige Operation in sensibler Berechnung erkannt\n");
    }
   
    // Ursprüngliche Umgebung wiederherstellen
    fesetenv(&env);
}

int main() {
    sensitive_calculation();
    return 0;
}

Ausnahmen vorübergehend maskieren

Beispiel

#include <fenv.h>
#include <stdio.h>
#include <math.h>

#pragma STDC FENV_ACCESS ON

void temp_disable_exceptions() {
    fenv_t env;
   
    // Umgebung speichern und alle Ausnahmen vorübergehend maskieren
    feholdexcept(&env);
   
    // Operationen ausführen, die Ausnahmen auslösen können
    double x = 0.0;
    double y = 1.0 / x;  // Es werden keine Ausnahmen ausgelöst
   
    printf("Eine Division durch Null wurde durchgeführt, aber nicht gemeldet\n");
   
    // Umgebung wiederherstellen und zuvor aufgetretene Ausnahmen behandeln
    feupdateenv(&env);
   
    // Nun können Ausnahmen geprüft werden
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Eine Division durch Null wurde nach Wiederherstellung der Umgebung erkannt\n");
    }
}

int main() {
    temp_disable_exceptions();
    return 0;
}

Beispiel für exakte Berechnung

Beispiel

#include <fenv.h>
#include <stdio.h>
#include <math.h>

#pragma STDC FENV_ACCESS ON

double precise_sum(double a, double b) {
    int original_round = fegetround();
    fenv_t env;
   
    // Umgebung speichern und höchsten Genauigkeitsmodus einstellen
    fegetenv(&env);
    fesetround(FE_TONEAREST);
    feclearexcept(FE_ALL_EXCEPT);
   
    double result = a + b;
   
    // Prüfen, ob die Berechnung exakt ist
    if (fetestexcept(FE_INEXACT)) {
        printf("Warnung: Summe war nicht exakt, möglicher Genauigkeitsverlust\n");
    }
   
    // Ursprüngliche Umgebung wiederherstellen
    fesetround(original_round);
    fesetenv(&env);
   
    return result;
}

int main() {
    double a = 1.0 / 3.0;
    double b = 2.0 / 3.0;
   
    printf("Summe: %.17g\n", precise_sum(a, b));
    return 0;
}

Wrapper für Ausnahmebehandlung

Beispiel

#include <fenv.h>
#include <stdio.h>
#include <math.h>

#pragma STDC FENV_ACCESS ON

typedef double (*math_func)(double);

double safe_math(math_func f, double x, int *error) {
    fenv_t env;
    *error = 0;
   
    feclearexcept(FE_ALL_EXCEPT);
    fegetenv(&env);
   
    double result = f(x);
   
    int exceptions = fetestexcept(FE_ALL_EXCEPT);
    if (exceptions) {
        *error = exceptions;
        if (exceptions & FE_INVALID) printf("Ungültige Operation\n");
        if (exceptions & FE_DIVBYZERO) printf("Division durch Null\n");
        if (exceptions & FE_OVERFLOW) printf("Überlauf\n");
        if (exceptions & FE_UNDERFLOW) printf("Unterlauf\n");
        if (exceptions & FE_INEXACT) printf("Ungenaues Ergebnis\n");
    }
   
    fesetenv(&env);
    return result;
}

int main() {
    int error;
   
    double a = safe_math(sqrt, -1.0, &error);
    printf("sqrt(-1) = %f (error: %d)\n", a, error);
   
    double b = safe_math(log, 0.0, &error);
    printf("log(0) = %f (error: %d)\n", b, error);
   
    return 0;
}

5, Hinweise

  • Bevor Funktionen der Gleitkommaumgebung verwendet werden, ist es normalerweise notwendig,#pragma STDC FENV_ACCESS ONzu aktivieren, um den Compiler darüber zu informieren, dass Operationen der Gleitkommaumgebung nicht wegoptimiert werden.

  • Nicht alle Implementierungen unterstützen alle Funktionen; vor der Verwendung sollte geprüft werden, ob das Makro FE_DFL_ENV definiert ist.

  • <fenv.h>Nur in C99 und späteren Versionen verfügbar.

  • Das spezifische Verhalten bei der Erkennung von Gleitkomma-Ausnahmen und der Steuerung des Rundungsmodus kann von der Hardware- und Compiler-Implementierung abhängen.

  • Auf einigen Plattformen können Gleitkomma-Ausnahmen standardmäßig maskiert sein, und sie müssenfeenableexceptdurch andere Funktionen explizit aktiviert werden.

  • Operationen der Gleitkommaumgebung können die Leistung beeinflussen und sollten mit Vorsicht verwendet werden.

<fenv.h>Bietet Steuerfunktionen für die Gleitkommaumgebung, einschließlich der Erkennung und Behandlung von Gleitkomma-Ausnahmen, der Einstellung von Rundungsmodi sowie der Abfrage und Änderung des Gleitkomma-Zustands. Es ist ein leistungsstarkes Werkzeug für hochpräzise Gleitkommaberechnungen und die Fehlersuche bei Gleitkommaproblemen, besonders geeignet für wissenschaftliches Rechnen und technische Anwendungen. Durch die Verwendung von<fenv.h>, Entwickler können das Verhalten von Gleitkommaoperationen besser steuern und so die Genauigkeit und Zuverlässigkeit der Berechnungsergebnisse gewährleisten.

Weitere Erweiterungen