Bibliothèque standard C<fenv.h>

Bibliothèque standard C<fenv.h>

<fenv.h>est un fichier d'en-tête de la bibliothèque standard C, utilisé pour contrôler l'environnement à virgule flottante.

<fenv.h>Introduit dans la norme C99, il fournit des fonctionnalités de contrôle et d'interrogation pour les exceptions de virgule flottante, les modes d'arrondi et d'autres états de virgule flottante.


<fenv.h>Le but principal est :

  • Détecter et gérer les exceptions de virgule flottante (telles que la division par zéro, le dépassement, etc.).

  • Contrôler le mode d'arrondi pour les opérations en virgule flottante (comme l'arrondi vers zéro, l'arrondi au plus proche, etc.).

  • Interroger et modifier les indicateurs d'état en virgule flottante.


1, Exceptions de virgule flottante

Les exceptions de virgule flottante sont des situations particulières qui se produisent lors d'opérations en virgule flottante, par exemple :

  • FE_DIVBYZERO: Division par zéro.

  • FE_INEXACT: Résultat inexact.

  • FE_INVALID: opération invalide (comme prendre la racine carrée d'un nombre négatif).

  • FE_OVERFLOW: Dépassement.

  • FE_UNDERFLOW: Sous-dépassement.

Ces exceptions sont représentées par des indicateurs d'état en virgule flottante, et peuvent être<fenv.h>Les fonctions qui s'y trouvent détectent et gèrent.


2, Modes d'arrondi

Le mode d'arrondi contrôle la manière dont les résultats des opérations en virgule flottante sont arrondis.<fenv.h>Les modes d'arrondi suivants sont définis :

  • FE_TONEAREST: arrondir à la valeur la plus proche (mode par défaut).

  • FE_DOWNWARD: arrondir vers l'infini négatif.

  • FE_UPWARD: arrondir vers l'infini positif.

  • FE_TOWARDZERO: arrondir vers zéro.


3, Fonctions et macros principales

<fenv.h>Fournit un ensemble de fonctions et de macros pour manipuler l'environnement en virgule flottante et gérer les exceptions en virgule flottante.

Gestion des exceptions en virgule flottante

Fonctions/Macros Description
feclearexcept(int excepts) Effacer les indicateurs d'exception de virgule flottante spécifiés
feraiseexcept(int excepts) Déclencher l'exception de virgule flottante spécifiée
fetestexcept(int excepts) Tester si l'exception de virgule flottante spécifiée s'est produite
fegetexceptflag(fexcept_t *flagp, int excepts) Obtenir l'état de l'indicateur d'exception de virgule flottante
fesetexceptflag(const fexcept_t *flagp, int excepts) Définir l'état de l'indicateur d'exception de virgule flottante

Contrôle du mode d'arrondi

Fonctions/Macros Description
fegetround(void) Obtenir le mode d'arrondi actuel
fesetround(int round) Définir le mode d'arrondi actuel

Contrôle de l'environnement de virgule flottante

Fonctions/Macros Description
fegetenv(fenv_t *envp) Sauvegarder l'environnement de virgule flottante actuel
fesetenv(const fenv_t *envp) Restaurer l'environnement de virgule flottante
feholdexcept(fenv_t *envp) Sauvegarder l'environnement de virgule flottante actuel et effacer les indicateurs d'exception.
feupdateenv(const fenv_t *envp) Restaurer l'environnement de virgule flottante et lever des exceptions

4, Exemple

Ce qui suit est un exemple utilisant<fenv.h>Un exemple de ceci, montrant comment détecter les exceptions de virgule flottante et contrôler le mode d'arrondi :

Exemple

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

int main() {
    // Activer la détection des exceptions de virgule flottante
    feclearexcept(FE_ALL_EXCEPT);

    // Déclencher une exception de division par zéro
    double x = 1.0, y = 0.0;
    double z = x / y;

    // Vérifier si une exception de division par zéro est survenue
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Une exception de division par zéro est survenue.\n");
    }

    // Définir le mode d'arrondi sur arrondi vers zéro
    fesetround(FE_TOWARDZERO);

    // Tester le mode d'arrondi
    double a = 1.5;
    double b = rint(a); // Arrondir à l'entier
    printf("Arrondi de 1.5 vers zéro : %.1f\n", b);

    return 0;
}

Le résultat de sortie est:

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

Détection des exceptions en virgule flottante

Exemple

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

#pragma STDC FENV_ACCESS ON

void check_exceptions() {
    // Effacer tous les indicateurs d'exception
    feclearexcept(FE_ALL_EXCEPT);
   
    // Effectuer des opérations pouvant lever des exceptions
    double x = 0.0;
    double y = 1.0 / x;  // Division par zéro
   
    // Vérifier la présence d'une exception spécifique
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Une division par zéro s'est produite !\n");
    }
   
    // Vérifier la présence d'une exception quelconque
    if (fetestexcept(FE_ALL_EXCEPT)) {
        printf("Au moins une exception en virgule flottante s'est produite\n");
    }
}

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

Contrôler le sens d'arrondi

Exemple

#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[] = {"Le plus proche", "Vers le haut", "Vers le bas", "Vers zéro"};
   
    for (int i = 0; i < 4; i++) {
        if (fesetround(modes[i]) == 0) {
            printf("Arrondi %s: %.1f -> %.0f\n",
                  names[i], num, rint(num));
        }
    }
}

int main() {
    test_rounding(2.5);  // Tester le résultat de 2.5 dans différents modes d'arrondi
    test_rounding(-2.5); // Tester le résultat de -2.5 dans différents modes d'arrondi
    return 0;
}

Sauvegarder et restaurer l'environnement à virgule flottante

Exemple

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

#pragma STDC FENV_ACCESS ON

void sensitive_calculation() {
    fenv_t env;
   
    // Sauvegarder l'environnement actuel
    fegetenv(&env);
   
    // Définir un environnement strict : intercepter toutes les exceptions
    feclearexcept(FE_ALL_EXCEPT);
   
    // Effectuer le calcul critique
    double result = sqrt(-1.0);  // Opération invalide
   
    if (fetestexcept(FE_INVALID)) {
        printf("Opération invalide détectée dans le calcul sensible\n");
    }
   
    // Restaurer l'environnement d'origine
    fesetenv(&env);
}

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

Masquage temporaire des exceptions

Exemple

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

#pragma STDC FENV_ACCESS ON

void temp_disable_exceptions() {
    fenv_t env;
   
    // Sauvegarder l'environnement et masquer temporairement toutes les exceptions
    feholdexcept(&env);
   
    // Effectuer des opérations pouvant lever des exceptions
    double x = 0.0;
    double y = 1.0 / x;  // Aucune exception ne sera déclenchée
   
    printf("Une division par zéro a été effectuée mais n'a pas été signalée\n");
   
    // Restaurer l'environnement et traiter les exceptions qui se sont produites précédemment
    feupdateenv(&env);
   
    // Les exceptions peuvent maintenant être vérifiées
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Une division par zéro a été détectée après la restauration de l'environnement\n");
    }
}

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

Exemple de calcul exact

Exemple

#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;
   
    // Sauvegarder l'environnement et définir le mode de précision le plus élevé
    fegetenv(&env);
    fesetround(FE_TONEAREST);
    feclearexcept(FE_ALL_EXCEPT);
   
    double result = a + b;
   
    // Vérifier si le calcul est exact
    if (fetestexcept(FE_INEXACT)) {
        printf("Avertissement : la somme n'était pas exacte, perte de précision possible\n");
    }
   
    // Restaurer l'environnement d'origine
    fesetround(original_round);
    fesetenv(&env);
   
    return result;
}

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

Enveloppe de gestion des exceptions

Exemple

#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("Opération non valide\n");
        if (exceptions & FE_DIVBYZERO) printf("Division par zéro\n");
        if (exceptions & FE_OVERFLOW) printf("Débordement\n");
        if (exceptions & FE_UNDERFLOW) printf("Sous-dépassement\n");
        if (exceptions & FE_INEXACT) printf("Résultat inexact\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, Notes

  • Avant d'utiliser les fonctionnalités de l'environnement à virgule flottante, il est généralement nécessaire d'activer#pragma STDC FENV_ACCESS ONpour informer le compilateur de ne pas optimiser les opérations de l'environnement à virgule flottante.

  • Toutes les implémentations ne prennent pas en charge toutes les fonctionnalités ; avant utilisation, vous devez vérifier si la macro FE_DFL_ENV est définie.

  • <fenv.h>Disponible uniquement en C99 et versions ultérieures.

  • Le comportement spécifique de la détection des exceptions à virgule flottante et du contrôle du mode d'arrondi peut dépendre du matériel et de l'implémentation du compilateur.

  • Sur certaines plateformes, les exceptions à virgule flottante peuvent être masquées par défaut, et doivent êtrefeenableexceptet d'autres fonctions activent explicitement.

  • Les opérations de l'environnement à virgule flottante peuvent affecter les performances et doivent être utilisées avec prudence.

<fenv.h>Fournit des fonctions de contrôle pour l'environnement à virgule flottante, y compris la détection et la gestion des exceptions à virgule flottante, le réglage des modes d'arrondi, et l'interrogation et la modification de l'état à virgule flottante. C'est un outil puissant pour le calcul à virgule flottante de haute précision et le débogage des problèmes à virgule flottante, particulièrement adapté au calcul scientifique et aux applications d'ingénierie. En utilisant<fenv.h>, les développeurs peuvent mieux contrôler le comportement des opérations en virgule flottante, garantissant ainsi l’exactitude et la fiabilité des résultats de calcul.

autres extensions