consteig
Compile-time eigenvalue and eigenvector computation for C++17
Loading...
Searching...
No Matches
utilities.hpp
1#ifndef CONSTMATH_UTILITIES_HPP
2#define CONSTMATH_UTILITIES_HPP
3
4namespace consteig
5{
6
10
11// These functions determine if a number is a floating point number
12
13template <typename T> struct is_float_impl
14{
15 static constexpr bool _()
16 {
17 return false;
18 }
19};
20
21template <> struct is_float_impl<float>
22{
23 static constexpr bool _()
24 {
25 return true;
26 }
27};
28
29template <> struct is_float_impl<double>
30{
31 static constexpr bool _()
32 {
33 return true;
34 }
35};
36
37template <> struct is_float_impl<long double>
38{
39 static constexpr bool _()
40 {
41 return true;
42 }
43};
44
47template <typename T> constexpr bool is_float(T const &)
48{
49 return is_float_impl<T>::_();
50}
51
63template <typename T> constexpr bool is_float()
64{
65 return is_float_impl<T>::_();
66}
67
79// Find machine epsilon
80// Implemented from wikipedia
81// https://en.wikipedia.org/wiki/Machine_epsilon#Approximation
82template <typename T> constexpr T epsilon()
83{
84 if constexpr (!is_float<T>())
85 {
86 return static_cast<T>(0);
87 }
88 else
89 {
90 // Hardcoded for standard IEEE 754 to ensure O(1) performance in
91 // recursion
92 if (sizeof(T) == sizeof(float))
93 {
94 return static_cast<T>(1.19209290e-7);
95 }
96 if (sizeof(T) == sizeof(double))
97 {
98 return static_cast<T>(2.2204460492503131e-16);
99 }
100
101 // Fallback for long double or others: calculate iteratively
102 T eps = static_cast<T>(1.0);
103 T one = static_cast<T>(1.0);
104 T half = static_cast<T>(0.5);
105
106 while ((one + (half * eps)) != one)
107 {
108 eps = half * eps;
109 }
110
111 return eps;
112 }
113}
114
116
117// This helper is intentionally NOT constexpr.
118// If a user attempts to evaluate a negative square root at compile-time
119// (e.g. `constexpr auto x = sqrt(-4);`), the compiler will hit this
120// non-constexpr function and halt the build with an error mentioning this
121// function's name. At runtime, it safely executes and returns a poison value.
122// For floating-point types, we return 0.0 / 0.0 to produce a true
123// self-poisoning NaN. For integer types, we use -1 as a poison value because
124// integer division by zero crashes at runtime, and constexpr NaN is not
125// portably supported in C++17.
126template <typename T> T force_compile_time_error_for_negative_sqrt()
127{
128 if constexpr (is_float<T>())
129 {
130 return static_cast<T>(0.0) / static_cast<T>(0.0);
131 }
132 else
133 {
134 return static_cast<T>(-1);
135 }
136}
137
138// Get a poison value representing an invalid result (like NaN).
139template <typename T> constexpr T poison_nan()
140{
141 // We use a non-constexpr helper to guarantee that this function cannot be
142 // evaluated at compile-time (triggering an error), but gracefully returns
143 // the poison value at runtime.
145}
146
147// Check if a value is the poison NaN.
148template <typename T> constexpr bool is_poison_nan(const T x)
149{
150 if constexpr (is_float<T>())
151 {
152 // In IEEE 754, NaN is the only value not equal to itself.
153 return x != x;
154 }
155 else
156 {
157 return x == static_cast<T>(-1);
158 }
159}
160
161} // namespace consteig
162
163#endif
constexpr bool is_float()
Returns true if T is a floating-point type (type-only overload).
Definition utilities.hpp:63
constexpr T epsilon()
Machine epsilon for type T.
Definition utilities.hpp:82