header_utils
Loading...
Searching...
No Matches
constexpr_math.h
1
4
5#pragma once
6
7#include <concepts>
8#include <limits>
9#include <cmath>
10#include <bit>
11
13namespace ghassanpl { namespace cem = ghassanpl::constexpr_math; }
14
16{
17 template <typename T>
18 concept arithmetic = std::is_arithmetic_v<T>;
19
20#ifdef __cpp_lib_constexpr_cmath
21 using std::pow;
22 using std::floor;
23 using std::sign;
24 using std::signbit;
25 using std::ceil;
26 using std::trunc;
27 using std::abs;
28 using std::fma;
29 using std::fmod;
30 using std::isnan;
31#else
32
36
37 [[nodiscard]] constexpr bool isnan(std::integral auto f) noexcept { return false; }
38 [[nodiscard]] constexpr bool isnan(float f) {
39 if (std::is_constant_evaluated())
40 return (std::bit_cast<uint32_t>(f) & 0x7FFFFFFFu) > 0x7F800000u;
41 else
42 return std::isnan(f);
43 }
44 [[nodiscard]] constexpr bool isnan(double f) {
45 if (std::is_constant_evaluated())
46 return (std::bit_cast<uint64_t>(f) & 0x7FFFFFFFFFFFFFFFull) > 0x7FF0000000000000ull;
47 else
48 return std::isnan(f);
49 }
50 [[nodiscard]] constexpr bool isfinite(std::integral auto f) noexcept { return true; }
51 [[nodiscard]] constexpr bool isfinite(float f) {
52 if (std::is_constant_evaluated())
53 return (std::bit_cast<uint32_t>(f) & 0x7F800000u) != 0x7F800000u;
54 else
55 return std::isfinite(f);
56 }
57 [[nodiscard]] constexpr bool isfinite(double f) {
58 if (std::is_constant_evaluated())
59 return (std::bit_cast<uint64_t>(f) & 0x7FF0000000000000ull) != 0x7FF0000000000000ull;
60 else
61 return std::isfinite(f);
62 }
63
65
66 template <std::floating_point T, typename RESULT = T>
67 [[nodiscard]] constexpr RESULT floor(T num) noexcept
68 {
69 if (std::is_constant_evaluated())
70 {
71 if (num == -std::numeric_limits<T>::infinity())
72 return static_cast<RESULT>(-std::numeric_limits<T>::infinity());
73 else if (num == std::numeric_limits<T>::infinity())
74 return static_cast<RESULT>(std::numeric_limits<T>::infinity());
75 else if (num != num)
76 return static_cast<RESULT>(std::numeric_limits<T>::quiet_NaN());
77
78 const auto res = static_cast<int64_t>(num);
79 return static_cast<RESULT>((res > num) ? res - 1 : res);
80 }
81 else
82 return std::floor(num);
83 }
84
85 template <typename T>
86 requires std::is_signed_v<T>
87 [[nodiscard]] constexpr bool signbit(T num)
88 {
89 if (std::is_constant_evaluated())
90 {
91 if constexpr (std::floating_point<T> && std::numeric_limits<T>::is_iec559)
92 {
94 if constexpr (sizeof(T) == sizeof(uint64_t))
95 return (std::bit_cast<uint64_t>(num) & 0x8000000000000000ull) != 0;
96 else if constexpr (sizeof(T) == sizeof(uint32_t))
97 return (std::bit_cast<uint32_t>(num) & 0x80000000u) != 0;
98 else if constexpr (sizeof(T) == sizeof(uint16_t))
99 return (std::bit_cast<uint16_t>(num) & 0x8000u) != 0;
100 else if constexpr (sizeof(T) == sizeof(uint8_t))
101 return (std::bit_cast<uint8_t>(num) & 0x80u) != 0;
102 else
103 static_assert(sizeof(T) == sizeof(uint8_t), "unsupported floating point type");
104 }
105 else
106 {
107 return num < T{};
108 }
109 }
110 else
111 return std::signbit(num);
112 }
113
114 template <typename T>
115 requires (!std::is_signed_v<T>)
116 [[nodiscard]] constexpr bool signbit(T val) noexcept
117 {
118 return val < T{};
119 }
120
121 template <typename T>
122 [[nodiscard]] constexpr int sign(T val)
123 {
124 return (T{} < val) - (val < T{});
125 /*
126 if (::ghassanpl::cem::signbit(val))
127 return -1;
128 else if (val == T{})
129 return 0;
130 return 1;
131 */
132 }
133
134 template <std::floating_point T, typename RESULT = T>
135 [[nodiscard]] constexpr RESULT ceil(T num) noexcept
136 {
137 if (std::is_constant_evaluated())
138 {
139 if (num == -std::numeric_limits<T>::infinity())
140 return static_cast<RESULT>(-std::numeric_limits<T>::infinity());
141 else if (num == std::numeric_limits<T>::infinity())
142 return static_cast<RESULT>(std::numeric_limits<T>::infinity());
143 else if (::ghassanpl::cem::isnan(num))
144 return static_cast<RESULT>(std::numeric_limits<T>::quiet_NaN());
145
146 const auto res = static_cast<int64_t>(num);
147 return static_cast<RESULT>((res >= num) ? res : res + 1);
148 }
149 else
150 return std::ceil(num);
151 }
152
153 template <std::floating_point T, typename RESULT = T>
154 [[nodiscard]] constexpr RESULT trunc(T num) noexcept
155 {
156 if (std::is_constant_evaluated())
157 return num < T{} ? -::ghassanpl::cem::floor<T, RESULT>(-num) : ::ghassanpl::cem::floor<T, RESULT>(num);
158 else
159 return std::trunc(num);
160 }
161
162 // TODO: round
163
164 template <arithmetic T>
165 [[nodiscard]] constexpr auto abs(T num)
166 {
167 if (std::is_constant_evaluated())
168 return ::ghassanpl::cem::signbit(num) ? -num : num;
169 else if constexpr (std::is_signed_v<T>)
170 return std::abs(num);
171 else
172 return num;
173 }
174
175 template <arithmetic T, arithmetic U, arithmetic V>
176 [[nodiscard]] constexpr auto fma(T a, U b, V c) noexcept
177 {
178 if (std::is_constant_evaluated())
179 return a * b + c;
180 else
181 {
182 using CT = std::conditional_t<
183 std::floating_point<T> || std::floating_point<U> || std::floating_point<V>,
184 decltype(a* b + c),
185 double
186 >;
187 return std::fma((CT)a, (CT)b, (CT)c);
188 }
189 }
190
191 template <arithmetic T, arithmetic U>
192 [[nodiscard]] constexpr auto fmod(T a, U b) noexcept
193 {
194 using CT = std::conditional_t<
195 std::floating_point<T> || std::floating_point<U>,
196 decltype(fma(trunc(a / b), -b, a)),
197 double
198 >;
199 if (std::is_constant_evaluated())
200 {
201 if (b == 0)
202 return std::numeric_limits<CT>::quiet_NaN();
203 else if (a == 0)
204 return CT{};
205 else if (a == std::numeric_limits<T>::infinity() || a == -std::numeric_limits<T>::infinity())
206 return std::numeric_limits<CT>::quiet_NaN();
207 else if (b == std::numeric_limits<U>::infinity() || b == -std::numeric_limits<U>::infinity())
208 return CT{};
209 else if (::ghassanpl::cem::isnan(a) || ::ghassanpl::cem::isnan(b))
210 return std::numeric_limits<CT>::quiet_NaN();
211
212 return ::ghassanpl::cem::fma(::ghassanpl::cem::trunc((CT)a / (CT)b), -(CT)b, (CT)a);
213 }
214 else
215 return std::fmod((CT)a, (CT)b);
216 }
217#endif
218
219 #include "constexpr_math.inl"
220
221 template <arithmetic T>
222 [[nodiscard]] constexpr auto sqrt(T value) noexcept
223 {
224 using CT = std::conditional_t<std::floating_point<T>, T, double>;
225 if (std::is_constant_evaluated())
226 {
227 static_assert(std::numeric_limits<double>::is_iec559, "sqrt only works for IEEE 754 floating point types");
228 return (CT)::ghassanpl::cem::detail::sqrt_impl((double)value);
229 }
230 else
231 return (CT)std::sqrt(value);
232 }
233
234 template <arithmetic T, arithmetic U>
235 [[nodiscard]] constexpr auto pow(T base, U exponent) noexcept
236 {
237 using CT = decltype(base * exponent);
238 if (std::is_constant_evaluated())
239 {
240 if constexpr (std::integral<T> && std::integral<U>)
241 {
242 CT result = static_cast<CT>(1);
243 while (exponent-- > 0)
244 result = result * base;
245 return result;
246 }
247 else
248 {
249 static_assert(std::numeric_limits<double>::is_iec559, "pow only works for IEEE 754 floating point types");
250 return (CT)::ghassanpl::cem::detail::pow_impl((double)base, (double)exponent);
251 }
252 }
253 else
254 return (CT)std::pow(base, exponent);
255 }
256
257 template <std::integral T>
258 [[nodiscard]] constexpr unsigned ilog2(T val) noexcept
259 {
260 unsigned result = 0;
261 if (val >= (1ULL << 32)) { result += 32; val >>= 32ULL; }
262 if (val >= (1ULL << 16)) { result += 16; val >>= 16ULL; }
263 if (val >= (1ULL << 8)) { result += 8; val >>= 8ULL; }
264 if (val >= (1ULL << 4)) { result += 4; val >>= 4ULL; }
265 if (val >= (1ULL << 2)) { result += 2; val >>= 2ULL; }
266 if (val >= (1ULL << 1)) result += 1;
267 return result;
268 }
269
270 [[nodiscard]] constexpr inline double unlerp(const double a, const double b, const double t) noexcept
271 {
273 return (t - a) / (b - a);
274 }
275
277}
Primary namespace for everything in this library.
Definition align+rec2.h:10