@@ -27,15 +27,6 @@ constexpr auto kNoSpecialCases = _NoSpecialCases{};
2727constexpr auto kNoExceptions = _NoExceptions{};
2828constexpr auto kLowAccuracy = _LowAccuracy{};
2929
30- // Rounding mode control
31- // Forces a specific rounding mode during computation
32- struct Round {
33- struct _Force {
34- static constexpr const char * kName = " kForce" ;
35- };
36- static constexpr auto kForce = _Force{};
37- };
38-
3930// Subnormal (denormal) number handling modes
4031// Controls how the CPU handles numbers smaller than the minimum normalized
4132// value
@@ -52,12 +43,34 @@ struct Subnormal {
5243
5344// Floating-point exception flags
5445// These match the standard C library FE_* macros
55- struct FPExceptions {
46+ class FPExceptions {
47+ public:
5648 static constexpr auto kNone = 0 ;
5749 static constexpr auto kInvalid = FE_INVALID;
5850 static constexpr auto kDivByZero = FE_DIVBYZERO;
5951 static constexpr auto kOverflow = FE_OVERFLOW;
6052 static constexpr auto kUnderflow = FE_UNDERFLOW;
53+
54+ void Raise (int errors) noexcept { mask_ |= errors; }
55+
56+ protected:
57+ void Load () noexcept {
58+ loaded_ = std::fegetexceptflag (&saved_, FE_ALL_EXCEPT) == 0 ;
59+ }
60+
61+ ~FPExceptions () noexcept {
62+ if (loaded_) {
63+ std::fesetexceptflag (&saved_, FE_ALL_EXCEPT);
64+ }
65+ if (mask_ != kNone ) {
66+ std::feraiseexcept (mask_);
67+ }
68+ }
69+
70+ private:
71+ bool loaded_ = false ;
72+ int mask_ = kNone ;
73+ std::fexcept_t saved_;
6174};
6275
6376/* *
@@ -84,7 +97,6 @@ struct FPExceptions {
8497 * - kNoLargeArgument: Skip extended precision reduction for large arguments
8598 * - kNoSpecialCases: Skip NaN/Inf handling (assumes finite inputs)
8699 * - kNoExceptions: Disable FP exception tracking for better performance
87- * - Round::kForce: Force round-to-nearest mode
88100 * - Subnormal::kDAZ/kFTZ: Flush subnormals to zero for performance
89101 * - Subnormal::kIEEE754: Strict IEEE 754 compliance (default if DAZ/FTZ not
90102 * specified)
@@ -109,23 +121,13 @@ struct FPExceptions {
109121 * ```
110122 */
111123template <typename ... Args>
112- class Precise {
124+ class Precise : public FPExceptions {
113125 public:
114126 // Default constructor saves current FP state
115127 Precise () noexcept {
116128 // Save exception flags unless disabled
117129 if constexpr (!kNoExceptions ) {
118- fegetexceptflag (&_exceptions, FE_ALL_EXCEPT);
119- }
120-
121- // Force rounding mode if requested
122- if constexpr (kRoundForce ) {
123- _rounding_mode = fegetround ();
124- int new_mode = _NewRoundingMode ();
125- if (_rounding_mode != new_mode) {
126- _retrieve_rounding_mode = true ;
127- fesetround (new_mode);
128- }
130+ FPExceptions::Load ();
129131 }
130132 }
131133
@@ -136,33 +138,8 @@ class Precise {
136138 // This constructor exists to enable Precise{tag1, tag2, ...} syntax
137139 }
138140
139- // Restore saved exception flags to FP environment
140- void FlushExceptions () noexcept {
141- if constexpr (!kNoExceptions ) {
142- fesetexceptflag (&_exceptions, FE_ALL_EXCEPT);
143- }
144- }
145-
146- // Record that an exception occurred (will be raised on destruction)
147- void Raise (int errors) noexcept {
148- static_assert (!kNoExceptions ,
149- " Cannot raise exceptions in NoExceptions mode" );
150- _exceptions |= errors;
151- }
152-
153- // Destructor restores original FP state
154- ~Precise () noexcept {
155- FlushExceptions ();
156- if constexpr (kRoundForce ) {
157- if (_retrieve_rounding_mode) {
158- fesetround (_rounding_mode);
159- }
160- }
161- }
162-
163141 // Compile-time configuration queries
164142 // These allow algorithms to optimize based on precision requirements
165-
166143 static constexpr bool kNoExceptions = (is_same_v<_NoExceptions, Args> || ...);
167144 static constexpr bool kNoLargeArgument =
168145 (is_same_v<_NoLargeArgument, Args> || ...);
@@ -176,9 +153,6 @@ class Precise {
176153 static constexpr bool kSpecialCases = !kNoSpecialCases ;
177154 static constexpr bool kExceptions = !kNoExceptions ;
178155
179- // Rounding mode configuration
180- static constexpr bool kRoundForce = (is_same_v<Round::_Force, Args> || ...);
181-
182156 // Subnormal handling configuration
183157 static constexpr bool kDAZ = (is_same_v<Subnormal::_DAZ, Args> || ...);
184158 static constexpr bool kFTZ = (is_same_v<Subnormal::_FTZ, Args> || ...);
@@ -193,16 +167,6 @@ class Precise {
193167
194168 // Default to IEEE754 if no subnormal mode specified
195169 static constexpr bool kIEEE754 = _kIEEE754 || !(kDAZ || kFTZ );
196-
197- private:
198- // Currently only supports round-to-nearest mode
199- // Could be extended to support other modes (toward zero, up, down)
200- int _NewRoundingMode () const { return FE_TONEAREST; }
201-
202- // Saved floating-point state
203- int _rounding_mode = 0 ;
204- bool _retrieve_rounding_mode = false ;
205- fexcept_t _exceptions; // Saved exception flags
206170}; // namespace npsr
207171
208172// Deduction guides for convenient construction
0 commit comments