Biblioteca estándar de C<fenv.h>
Biblioteca estándar de C<fenv.h>
<fenv.h>Es un archivo de encabezado de la biblioteca estándar de C que se utiliza para controlar el entorno de punto flotante (Floating-Point Environment).
<fenv.h>Introducido en el estándar C99, proporciona funciones para controlar y consultar excepciones de punto flotante, modos de redondeo y otros estados de punto flotante.
<fenv.h>Su propósito principal es:
-
Detectar y manejar excepciones de punto flotante (como división por cero, desbordamiento, etc.).
-
Controlar el modo de redondeo de las operaciones de punto flotante (como redondeo hacia cero, redondeo al más cercano, etc.).
-
Consultar y modificar los indicadores de estado de punto flotante.
1, Excepciones de punto flotante
Las excepciones de punto flotante son situaciones especiales que ocurren en operaciones de punto flotante, por ejemplo:
-
FE_DIVBYZERO: división por cero.
-
FE_INEXACT: resultado inexacto.
-
FE_INVALID: operación no válida (como la raíz cuadrada de un número negativo).
-
FE_OVERFLOW: desbordamiento (overflow).
-
FE_UNDERFLOW: subdesbordamiento (underflow).
Estas excepciones se representan mediante indicadores de estado de punto flotante y pueden<fenv.h>detectarse y manejarse con las funciones de <fenv.h>.
2, Modo de redondeo
El modo de redondeo controla la forma en que se redondean los resultados de las operaciones de punto flotante.<fenv.h>Se definen los siguientes modos de redondeo:
-
FE_TONEAREST: redondear al valor más cercano (modo predeterminado).
-
FE_DOWNWARD: redondear hacia el infinito negativo.
-
FE_UPWARD: redondear hacia el infinito positivo.
-
FE_TOWARDZERO: redondear hacia cero.
3, Funciones y macros principales
<fenv.h>Proporciona un conjunto de funciones y macros para manipular el entorno de punto flotante y manejar las excepciones de punto flotante.
Manejo de excepciones de punto flotante
| Función/Macro | Descripción |
|---|---|
feclearexcept(int excepts) |
Limpia los indicadores de excepción de punto flotante especificados. |
feraiseexcept(int excepts) |
Dispara la excepción de punto flotante especificada. |
fetestexcept(int excepts) |
Comprueba si se ha producido la excepción de punto flotante especificada. |
fegetexceptflag(fexcept_t *flagp, int excepts) |
Obtiene el estado de los indicadores de excepción de punto flotante. |
fesetexceptflag(const fexcept_t *flagp, int excepts) |
Establece el estado de los indicadores de excepción de punto flotante. |
Control del modo de redondeo
| Función/Macro | Descripción |
|---|---|
fegetround(void) |
Obtiene el modo de redondeo actual. |
fesetround(int round) |
Establece el modo de redondeo actual. |
Control del entorno de punto flotante
| Función/Macro | Descripción |
|---|---|
fegetenv(fenv_t *envp) |
Guarda el entorno de punto flotante actual. |
fesetenv(const fenv_t *envp) |
Restaura el entorno de punto flotante. |
feholdexcept(fenv_t *envp) |
Guarda el entorno de punto flotante actual y limpia los indicadores de excepción. |
feupdateenv(const fenv_t *envp) |
Restaura el entorno de punto flotante y dispara las excepciones. |
4, Ejemplo
A continuación se muestra un ejemplo del uso de<fenv.h>la biblioteca, que demuestra cómo detectar excepciones de punto flotante y controlar el modo de redondeo:
Ejemplo
#include <fenv.h>
#include <math.h>
int main() {
// Habilitar la detección de excepciones de punto flotante
feclearexcept(FE_ALL_EXCEPT);
// Disparar la excepción de división por cero
double x = 1.0, y = 0.0;
double z = x / y;
// Comprobar si se ha producido la excepción de división por cero
if (fetestexcept(FE_DIVBYZERO)) {
printf("Divide by zero exception occurred.\n");
}
// Establecer el modo de redondeo hacia cero
fesetround(FE_TOWARDZERO);
// Probar el modo de redondeo
double a = 1.5;
double b = rint(a); // Redondear a entero
printf("Rounding 1.5 toward zero: %.1f\n", b);
return 0;
}
El resultado de salida es:
Divide by zero exception occurred. Rounding 1.5 toward zero: 1.0
Detección de excepciones de punto flotante
Ejemplo
#include <stdio.h>
#include <math.h>
#pragma STDC FENV_ACCESS ON
void check_exceptions() {
// Limpiar todos los indicadores de excepción
feclearexcept(FE_ALL_EXCEPT);
// Realizar operaciones que puedan provocar excepciones
double x = 0.0;
double y = 1.0 / x; // División por cero
// Verificar una excepción específica
if (fetestexcept(FE_DIVBYZERO)) {
printf("Division by zero occurred!\n");
}
// Verificar cualquier excepción
if (fetestexcept(FE_ALL_EXCEPT)) {
printf("At least one floating-point exception occurred\n");
}
}
int main() {
check_exceptions();
return 0;
}
Control de la dirección de redondeo
Ejemplo
#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[] = {"Nearest", "Up", "Down", "Toward zero"};
for (int i = 0; i < 4; i++) {
if (fesetround(modes[i]) == 0) {
printf("Rounding %s: %.1f -> %.0f\n",
names[i], num, rint(num));
}
}
}
int main() {
test_rounding(2.5); // Probar el resultado de 2.5 en diferentes modos de redondeo
test_rounding(-2.5); // Probar el resultado de -2.5 en diferentes modos de redondeo
return 0;
}
Guardar y restaurar el entorno de punto flotante
Ejemplo
#include <stdio.h>
#include <math.h>
#pragma STDC FENV_ACCESS ON
void sensitive_calculation() {
fenv_t env;
// Guardar el entorno actual
fegetenv(&env);
// Establecer entorno estricto: capturar todas las excepciones
feclearexcept(FE_ALL_EXCEPT);
// Realizar cálculos críticos
double result = sqrt(-1.0); // Operación no válida
if (fetestexcept(FE_INVALID)) {
printf("Invalid operation detected in sensitive calculation\n");
}
// Restaurar el entorno original
fesetenv(&env);
}
int main() {
sensitive_calculation();
return 0;
}
Enmascaramiento temporal de excepciones
Ejemplo
#include <stdio.h>
#include <math.h>
#pragma STDC FENV_ACCESS ON
void temp_disable_exceptions() {
fenv_t env;
// Guardar el entorno y enmascarar temporalmente todas las excepciones
feholdexcept(&env);
// Realizar operaciones que puedan generar excepciones
double x = 0.0;
double y = 1.0 / x; // No se disparará ninguna excepción
printf("Division by zero was performed but not reported\n");
// Restaurar el entorno y manejar las excepciones ocurridas previamente
feupdateenv(&env);
// Ahora se pueden verificar las excepciones
if (fetestexcept(FE_DIVBYZERO)) {
printf("Division by zero was detected after restoring environment\n");
}
}
int main() {
temp_disable_exceptions();
return 0;
}
Ejemplo de cálculo de alta precisión
Ejemplo
#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;
// Guardar el entorno y establecer el modo de máxima precisión
fegetenv(&env);
fesetround(FE_TONEAREST);
feclearexcept(FE_ALL_EXCEPT);
double result = a + b;
// Verificar si el cálculo es exacto
if (fetestexcept(FE_INEXACT)) {
printf("Warning: Sum was not exact, possible precision loss\n");
}
// Restaurar el entorno original
fesetround(original_round);
fesetenv(&env);
return result;
}
int main() {
double a = 1.0 / 3.0;
double b = 2.0 / 3.0;
printf("Sum: %.17g\n", precise_sum(a, b));
return 0;
}
Wrapper de manejo de excepciones
Ejemplo
#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("Invalid operation\n");
if (exceptions & FE_DIVBYZERO) printf("Division by zero\n");
if (exceptions & FE_OVERFLOW) printf("Overflow\n");
if (exceptions & FE_UNDERFLOW) printf("Underflow\n");
if (exceptions & FE_INEXACT) printf("Inexact result\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, Notas importantes
Antes de usar las funciones del entorno de punto flotante, normalmente es necesario habilitar#pragma STDC FENV_ACCESS ON, para indicar al compilador que no optimice las operaciones del entorno de punto flotante.
-
No todas las implementaciones admiten todas las funciones; antes de usarlas, se debe comprobar si la macro FE_DFL_ENV está definida.
-
<fenv.h>Solo está disponible en C99 y versiones posteriores. -
El comportamiento específico de la detección de excepciones de punto flotante y el control del modo de redondeo puede depender del hardware y de la implementación del compilador.
-
En algunas plataformas, las excepciones de punto flotante pueden estar enmascaradas por defecto; es necesario
feenableexcepthabilitarlas explícitamente mediante funciones como feclearexcept() y otras. -
Las operaciones del entorno de punto flotante pueden afectar al rendimiento; deben usarse con precaución.
<fenv.h>Proporciona funciones de control del entorno de punto flotante, incluida la detección y el manejo de excepciones de punto flotante, el establecimiento del modo de redondeo y la consulta y modificación del estado de punto flotante. Es una herramienta potente para realizar cálculos de punto flotante de alta precisión y depurar problemas de punto flotante, especialmente adecuada para aplicaciones científicas y de ingeniería. Mediante el uso de<fenv.h>, los desarrolladores pueden controlar mejor el comportamiento de las operaciones de punto flotante y garantizar la exactitud y fiabilidad de los resultados de los cálculos.