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 <stdio.h>
#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 <fenv.h>
#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 <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[] = {"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 <fenv.h>
#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 <fenv.h>
#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 <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;
   
    // 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 <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("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 necesariofeenableexcepthabilitarlas 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.

Otras extensiones