CoolProp 8.0.0
An open-source fluid property and humid air property database
FPUGuard.h
Go to the documentation of this file.
1#ifndef COOLPROP_FPUGUARD_H
2#define COOLPROP_FPUGUARD_H
3
4// RAII guard that masks IEEE-754 floating-point exceptions on construction and
5// restores the caller's FP environment on destruction.
6//
7// CoolProp uses NaN and _HUGE (HUGE_VAL) as in-band sentinels. Arithmetic and
8// comparisons on those values set the FP exception *status* flags (FE_INVALID,
9// FE_OVERFLOW, FE_DIVBYZERO). Host environments react two ways:
10// * Polling hosts (Excel/VBA) read the status word after a call returns and
11// report a spurious FP error.
12// * Trapping hosts (Delphi, some TRNSYS/Fortran builds) run with FP
13// exceptions *unmasked*, so the CPU raises a hardware trap / SIGFPE at the
14// offending instruction -- mid-calculation, before any cleanup can run.
15//
16// Clearing the status flags on exit (the historical fpu_reset_guard behavior)
17// only helps polling hosts. This guard additionally *masks* the exceptions on
18// entry so trapping hosts do not fault, then restores the caller's environment
19// on exit (which also wipes any flags we raised, covering the polling case).
20//
21// Windows is the platform that hurts most here: Delphi's RTL *unmasks* FP
22// exceptions by default, and Excel/VBA poll the status word. So the Windows
23// masking path is keyed on _WIN32 (any compiler), not just _MSC_VER, so that
24// MinGW-built DLLs are protected too -- _controlfp_s lives in <float.h> for
25// both MSVC and MinGW-w64.
26//
27// See GitHub issue #3012 and bd CoolProp-i3o.
28
29#if defined(_WIN32)
30# include <float.h> // _controlfp_s, _clearfp, _MCW_EM (MS CRT extension)
31# define COOLPROP_FPUGUARD_WIN
32#elif defined(__GLIBC__)
33# include <cfenv> // fegetexcept/fedisableexcept/feenableexcept are glibc-only
34# if defined(FE_ALL_EXCEPT)
35# define COOLPROP_FPUGUARD_GLIBC
36# endif
37#else
38# include <cfenv> // standard feclearexcept only (no portable masking API)
39#endif
40
41namespace CoolProp {
42
44{
45 public:
47#if defined(COOLPROP_FPUGUARD_WIN)
48 // Read the current control word, then set every exception-mask bit
49 // (_MCW_EM): a set bit *disables* (masks) that exception's trap.
50 unsigned int dummy = 0;
51 _controlfp_s(&saved_cw, 0, 0); // snapshot current control word
52 _controlfp_s(&dummy, _MCW_EM, _MCW_EM); // mask all FP exceptions
53#elif defined(COOLPROP_FPUGUARD_GLIBC)
54 // fegetexcept() returns the set of currently *enabled* (trapping)
55 // exceptions; remember it so we can restore the caller's choice.
56 saved_excepts = fegetexcept();
57 fedisableexcept(FE_ALL_EXCEPT); // mask all
58#endif
59 // Platforms without a portable masking API (macOS/BSD, PowerPC) fall
60 // through with no masking; they do not enable FP traps by default, so
61 // clearing status flags on exit (see ~fpu_guard) is the sufficient,
62 // historical mitigation there.
63 }
64 fpu_guard(const fpu_guard&) = delete;
65 fpu_guard(fpu_guard&&) = delete;
66 fpu_guard& operator=(const fpu_guard&) = delete;
69#if defined(COOLPROP_FPUGUARD_WIN)
70 _clearfp(); // clear flags we raised
71 unsigned int dummy = 0;
72 _controlfp_s(&dummy, saved_cw, _MCW_EM); // restore caller's masking
73#elif defined(COOLPROP_FPUGUARD_GLIBC)
74 feclearexcept(FE_ALL_EXCEPT); // clear flags we raised
75 feenableexcept(saved_excepts); // restore caller's trapping set
76#elif defined(FE_ALL_EXCEPT)
77 feclearexcept(FE_ALL_EXCEPT); // fallback: clear status flags only
78#endif
79 }
80
81 private:
82#if defined(COOLPROP_FPUGUARD_WIN)
83 unsigned int saved_cw{0};
84#elif defined(COOLPROP_FPUGUARD_GLIBC)
85 int saved_excepts{0};
86#endif
87};
88
89} // namespace CoolProp
90
91#endif // COOLPROP_FPUGUARD_H