<cfenv>
C++11 header for IEEE 754 floating-point environment control — exception flags, rounding modes, and environment save/restore.
<cfenv>since C++11A C++ wrapper around C99's <fenv.h> that exposes IEEE 754 floating-point exception flags, rounding mode control, and full environment save/restore via types, macros, and functions placed into namespace std.
Overview
Modern hardware implementing IEEE 754 tracks five exception conditions as sticky status flags: invalid operation, division by zero, overflow, underflow, and inexact result. <cfenv> lets you inspect and manipulate these flags programmatically, switch the active rounding mode at runtime, and checkpoint the entire floating-point environment so library code does not leak state changes to its callers.
Compiler cooperation is required. Under the C++ as-if rule, compilers may reorder or eliminate floating-point operations freely. To make FP state visible as observable side effects, you must place #pragma STDC FENV_ACCESS ON before any code that reads or writes the FP environment. Without it, an optimizing compiler is permitted — and in practice often does — eliminate flag-setting operations entirely. GCC additionally requires -frounding-math when dynamically changing the rounding mode; Clang requires -frounding-math or -fno-unsafe-math-optimizations for the same reason.
All identifiers are in namespace std. The unqualified C names from <fenv.h> may also be visible, but should be treated as implementation details.
Exception flags are sticky. Once an IEEE 754 exception occurs, its flag remains set until explicitly cleared. Flags do not trap by default in standard C++. POSIX extends the interface with feenableexcept / fedisableexcept to install trap handlers, but those are non-standard and absent on Windows and many embedded targets.
Syntax
#include <cfenv>
// Opaque types
std::fenv_t // complete floating-point environment state
std::fexcept_t // exception flag state (a subset of fenv_t)
// Exception flag macros (a platform may define any subset; unsupported ones == 0)
FE_DIVBYZERO // x / 0.0 where x is finite and non-zero
FE_INEXACT // result is not exactly representable
FE_INVALID // e.g. 0.0/0.0, sqrt(-1.0), NaN comparisons
FE_OVERFLOW // result magnitude exceeds the representable range
FE_UNDERFLOW // result magnitude is too small (subnormal or flushed to zero)
FE_ALL_EXCEPT // bitwise OR of all supported exception macros
// Rounding mode macros
FE_DOWNWARD // toward -∞
FE_TONEAREST // round-half-to-even (IEEE 754 default, banker's rounding)
FE_TOWARDZERO // toward zero (truncation)
FE_UPWARD // toward +∞
FE_DFL_ENV // pointer to const fenv_t representing the default environment
// Exception flag functions
int std::feclearexcept(int excepts); // clear flags
int std::feraiseexcept(int excepts); // raise flags
int std::fetestexcept(int excepts); // test flags (returns set subset)
int std::fegetexceptflag(std::fexcept_t* flagp, int excepts); // snapshot flags
int std::fesetexceptflag(const std::fexcept_t* flagp, int excepts); // restore snapshot
// Rounding mode
int std::fegetround(); // returns current rounding mode macro, or -1
int std::fesetround(int round); // 0 on success
// Full environment save/restore
int std::fegetenv(std::fenv_t* envp); // snapshot env (flags + rounding + more)
int std::fesetenv(const std::fenv_t* envp); // restore env verbatim
int std::feholdexcept(std::fenv_t* envp); // snapshot, clear all flags, non-stop mode
int std::feupdateenv(const std::fenv_t* envp); // restore env; re-raise pre-existing exceptionsEvery function returns zero on success and non-zero on failure. Failure typically means the operation is unsupported on the current platform.
Examples
Detecting exceptions from a numeric computation
#pragma STDC FENV_ACCESS ON
#include <cfenv>
#include <cmath>
#include <cstdio>
// math_errhandling & MATH_ERREXCEPT must be non-zero for <cmath>
// functions to actually raise FP exception flags (true on most hosted platforms).
void probe(double x) {
std::feclearexcept(FE_ALL_EXCEPT); // flags are sticky — clear before the region
double r = std::log(x);
if (std::fetestexcept(FE_INVALID)) { std::puts("invalid: argument < 0"); return; }
if (std::fetestexcept(FE_DIVBYZERO)) { std::puts("pole: log(0) = -inf"); return; }
if (std::fetestexcept(FE_OVERFLOW)) { std::puts("overflow"); return; }
std::printf("log(%g) = %.17g (inexact=%d)\n",
x, r, !!(std::fetestexcept(FE_INEXACT)));
}
int main() {
probe(-1.0); // FE_INVALID
probe(0.0); // FE_DIVBYZERO
probe(1.0); // exact; FE_INEXACT not set
probe(1e300); // FE_INEXACT
}Directed rounding for interval arithmetic
Switching rounding mode before each operation guarantees conservative (outer) bounds. This is the foundation of validated numerics.
#pragma STDC FENV_ACCESS ON
#include <cfenv>
#include <cstdio>
struct Interval { double lo, hi; };
Interval iadd(Interval a, Interval b) {
double lo, hi;
std::fesetround(FE_DOWNWARD); // C++11 — round toward -∞ for lower bound
lo = a.lo + b.lo;
std::fesetround(FE_UPWARD); // C++11 — round toward +∞ for upper bound
hi = a.hi + b.hi;
std::fesetround(FE_TONEAREST); // always restore default before returning
return {lo, hi};
}
Interval imul(Interval a, Interval b) {
// For positive intervals only (full case requires checking all four products)
double lo, hi;
std::fesetround(FE_DOWNWARD); lo = a.lo * b.lo;
std::fesetround(FE_UPWARD); hi = a.hi * b.hi;
std::fesetround(FE_TONEAREST);
return {lo, hi};
}
int main() {
// 0.1 + 0.2 is not exactly representable; the interval must contain 0.3
Interval x{0.1, 0.1}, y{0.2, 0.2};
auto z = iadd(x, y);
std::printf("[%.17g, %.17g]\n", z.lo, z.hi);
// [0.29999999999999998, 0.30000000000000004] — contains 0.3
}RAII guard for exception-transparent library code
Library functions that modify the FP environment must restore it on exit. The idiomatic approach is a RAII guard built on feholdexcept / feupdateenv.
#pragma STDC FENV_ACCESS ON
#include <cfenv>
#include <cmath>
#include <stdexcept>
class FenvGuard {
std::fenv_t saved_;
public:
FenvGuard() { std::feholdexcept(&saved_); } // save, clear all flags, non-stop mode
~FenvGuard() { std::feupdateenv(&saved_); } // restore; re-raise pre-existing exceptions
FenvGuard(const FenvGuard&) = delete;
FenvGuard& operator=(const FenvGuard&) = delete;
};
// Caller's FP environment is fully preserved regardless of what happens inside.
double hypot_safe(double a, double b) {
FenvGuard guard;
std::fesetround(FE_TONEAREST);
double r = std::sqrt(a * a + b * b);
if (std::fetestexcept(FE_OVERFLOW)) {
// Handle internally before guard destructor restores caller's state
r = std::numeric_limits<double>::infinity();
}
return r;
}feholdexcept differs critically from fegetenv: it also clears all exception flags and installs a non-stop mode so that FP exceptions inside the guarded block do not propagate. feupdateenv differs from fesetenv: it restores the saved environment and then re-raises any exceptions that had been pending in the saved state before our code ran — preserving the caller's pre-existing flags.
Best Practices
Always use feholdexcept + feupdateenv in pairs. Using fesetenv to restore after fegetenv silently discards both the pre-existing exception flags from the caller and any raised inside your function. feholdexcept / feupdateenv is the only standard combination that correctly isolates internal exceptions while preserving the caller's state.
Clear flags immediately before the region under test. Exception flags are sticky and accumulate across all prior operations. Calling feclearexcept(FE_ALL_EXCEPT) at function entry is not sufficient if unrelated operations occur between entry and the region you care about.
Check math_errhandling before relying on exception flags. The macro math_errhandling is a bitfield. If math_errhandling & MATH_ERREXCEPT is zero, <cmath> functions signal errors via errno only, and FP exception flags will not be raised. On such platforms, use errno checking instead.
Restore the rounding mode before every return path. If a function can throw or return early, restore FE_TONEAREST on every path. The RAII guard above handles this automatically.
Common Pitfalls
Omitting #pragma STDC FENV_ACCESS ON. Without this pragma, the implementation is allowed to treat FP flags as dead stores and optimize them away. With -O2 or higher, GCC and Clang will do exactly this in common cases. Support for the pragma varies: GCC respects it as of GCC 7+; Clang respects it but may emit a warning on older versions.
Confusing fesetenv with feupdateenv. fesetenv is a verbatim replace: it overwrites the environment, including any exception flags the caller had accumulated before the call. feupdateenv re-raises those pre-existing flags after restoring. Mixing them up silently corrupts the caller's exception state.
Assuming all exception macros are non-zero. IEEE 754 support is implementation-defined. On a platform where FE_UNDERFLOW == 0, calling fetestexcept(FE_UNDERFLOW) always returns zero, even if an underflow actually occurred. Portable code must test fetestexcept(excepts) & excepts after verifying the macro is non-zero.
Using feenableexcept / fedisableexcept portably. These POSIX extensions install SIGFPE trap handlers on FP exceptions. They are unavailable on Windows and many embedded toolchains and have no equivalent in ISO C++. Code using them is non-portable and must be conditionally compiled.
Ignoring return values. All fe* functions return non-zero to indicate unsupported operations. On targets without IEEE 754 hardware (certain microcontrollers or soft-float ABIs), most operations silently fail. Ignoring the return value means your code will appear to work correctly in test environments but produce wrong answers silently in production on different hardware.