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