C Standard Library<fenv.h>

C Standard Library<fenv.h>

<fenv.h>is a header file in the C standard library, used to control the floating-point environment.

<fenv.h>Introduced in the C99 standard, it provides control and query functionality for floating-point exceptions, rounding modes, and other floating-point state.


<fenv.h>The main purpose is:

  • Detect and handle floating-point exceptions (such as division by zero, overflow, etc.).

  • Control the rounding mode for floating-point operations (such as round toward zero, round to nearest, etc.).

  • Query and modify floating-point status flags.


1、Floating-point exceptions

Floating-point exceptions are special situations that occur during floating-point operations, for example:

  • FE_DIVBYZERO: Divide by zero.

  • FE_INEXACT: Inexact result.

  • FE_INVALID: invalid operation (such as taking the square root of a negative number).

  • FE_OVERFLOW: Overflow.

  • FE_UNDERFLOW: Underflow.

These exceptions are represented by floating-point status flags, and can be<fenv.h>The functions therein detect and handle.


2、Rounding modes

The rounding mode controls the way floating-point operation results are rounded.<fenv.h>The following rounding modes are defined:

  • FE_TONEAREST: round to the nearest value (default mode).

  • FE_DOWNWARD: round toward negative infinity.

  • FE_UPWARD: round toward positive infinity.

  • FE_TOWARDZERO: Round toward zero.


3、Main functions and macros

<fenv.h>Provides a set of functions and macros for manipulating the floating-point environment and handling floating-point exceptions.

Floating-point exception handling

Functions/Macros Description
feclearexcept(int excepts) Clear the specified floating-point exception flags
feraiseexcept(int excepts) Raise the specified floating-point exception
fetestexcept(int excepts) Test whether the specified floating-point exception occurred
fegetexceptflag(fexcept_t *flagp, int excepts) Get the floating-point exception flag status
fesetexceptflag(const fexcept_t *flagp, int excepts) Set the floating-point exception flag status

Rounding mode control

Functions/Macros Description
fegetround(void) Get the current rounding mode
fesetround(int round) Set the current rounding mode

Floating-point environment control

Functions/Macros Description
fegetenv(fenv_t *envp) Save the current floating-point environment
fesetenv(const fenv_t *envp) Restoring the floating-point environment
feholdexcept(fenv_t *envp) Save the current floating-point environment and clear exception flags.
feupdateenv(const fenv_t *envp) Restore the floating-point environment and raise exceptions

4、Example

The following is an example using<fenv.h>An example of this, demonstrating how to detect floating-point exceptions and control the rounding mode:

Example

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

int main() {
    // Enable floating-point exception detection
    feclearexcept(FE_ALL_EXCEPT);

    // Trigger divide-by-zero exception
    double x = 1.0, y = 0.0;
    double z = x / y;

    // Check whether a divide-by-zero exception occurred
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Divide by zero exception occurred.\n");
    }

    // Set rounding mode to round toward zero
    fesetround(FE_TOWARDZERO);

    // Test rounding mode
    double a = 1.5;
    double b = rint(a); // Round to integer
    printf("Rounding 1.5 toward zero: %.1f\n", b);

    return 0;
}

The output result is:

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

Detecting floating-point exceptions

Example

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

#pragma STDC FENV_ACCESS ON

void check_exceptions() {
    // Clear all exception flags
    feclearexcept(FE_ALL_EXCEPT);
   
    // Perform operations that may raise exceptions
    double x = 0.0;
    double y = 1.0 / x;  // Divide by zero
   
    // Check for a specific exception
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Division by zero occurred!\n");
    }
   
    // Check for any exception
    if (fetestexcept(FE_ALL_EXCEPT)) {
        printf("At least one floating-point exception occurred\n");
    }
}

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

Controlling rounding direction

Example

#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);  // Test the result of 2.5 in different rounding modes
    test_rounding(-2.5); // Test the result of -2.5 in different rounding modes
    return 0;
}

Save and restore the floating-point environment

Example

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

#pragma STDC FENV_ACCESS ON

void sensitive_calculation() {
    fenv_t env;
   
    // Save the current environment
    fegetenv(&env);
   
    // Set strict environment: catch all exceptions
    feclearexcept(FE_ALL_EXCEPT);
   
    // Perform critical calculation
    double result = sqrt(-1.0);  // Invalid operation
   
    if (fetestexcept(FE_INVALID)) {
        printf("Invalid operation detected in sensitive calculation\n");
    }
   
    // Restore original environment
    fesetenv(&env);
}

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

Temporarily masking exceptions

Example

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

#pragma STDC FENV_ACCESS ON

void temp_disable_exceptions() {
    fenv_t env;
   
    // Save environment and temporarily mask all exceptions
    feholdexcept(&env);
   
    // Perform operations that may raise exceptions
    double x = 0.0;
    double y = 1.0 / x;  // No exceptions will be triggered
   
    printf("Division by zero was performed but not reported\n");
   
    // Restore environment and handle exceptions that occurred previously
    feupdateenv(&env);
   
    // Now exceptions can be checked
    if (fetestexcept(FE_DIVBYZERO)) {
        printf("Division by zero was detected after restoring environment\n");
    }
}

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

Exact computation example

Example

#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;
   
    // Save environment and set highest precision mode
    fegetenv(&env);
    fesetround(FE_TONEAREST);
    feclearexcept(FE_ALL_EXCEPT);
   
    double result = a + b;
   
    // Check whether the calculation is exact
    if (fetestexcept(FE_INEXACT)) {
        printf("Warning: Sum was not exact, possible precision loss\n");
    }
   
    // Restore original environment
    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;
}

Exception handling wrapper

Example

#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、Notes

  • Before using floating-point environment features, it is usually necessary to enable#pragma STDC FENV_ACCESS ONto inform the compiler not to optimize away floating-point environment operations.

  • Not all implementations support all features; before use, you should check whether the macro FE_DFL_ENV is defined.

  • <fenv.h>Only available in C99 and later versions.

  • The specific behavior of floating-point exception detection and rounding mode control may depend on the hardware and compiler implementation.

  • On some platforms, floating-point exceptions may be masked by default, and need to befeenableexceptand other functions explicitly enable.

  • Floating-point environment operations may affect performance and should be used with caution.

<fenv.h>Provides control functions for the floating-point environment, including detection and handling of floating-point exceptions, setting of rounding modes, and querying and modification of floating-point state. It is a powerful tool for high-precision floating-point computation and debugging floating-point problems, especially suitable for scientific computing and engineering applications. By using<fenv.h>, developers can better control the behavior of floating-point operations, ensuring the accuracy and reliability of computation results.

other extensions