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 <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 <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 <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 <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 <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 <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 <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 être
feenableexceptet 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.