Not Enough Math
Lightweight zero-dependency C++ math library
Loading...
Searching...
No Matches
utils.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <concepts>
4#include <type_traits>
5#include <cassert>
6
7#include "consts.hpp"
8#include "err.hpp"
9#include "config.hpp"
10
11#include "intrinsics.hpp"
12
13namespace nem
14{
18
19 template <std::floating_point T> constexpr T is_infinite(T value) noexcept { return !nem::intr::_nem_isfinite(value); }
20 template <std::floating_point T> constexpr T is_nan(T value) noexcept { return !nem::intr::_nem_isfinite(value); }
21
22 template <std::integral T> constexpr T is_infinite(T) noexcept { return false; }
23 template <std::integral T> constexpr T is_nan(T) noexcept { return false; }
24
25
29
31 template <std::floating_point T> constexpr T copysign(T to, T from) noexcept
32 {
33 //to = static_cast<T>(__builtin_copysign(static_cast<double>(to), static_cast<double>(from)));
34 return nem::intr::_nem_copysign(to, from);
35 }
36 template <nem::scalar_type T> constexpr T sign(T value) noexcept { return nem::copysign<T>((T)1.0, value); }
37
38 template <typename T> constexpr T abs(T value) noexcept { return T{ value >= 0 ? value : -value }; }
39 template <std::floating_point T> constexpr T abs(T value) noexcept { return nem::intr::_nem_fabs(value); }
40
44
48 template <nem::scalar_type T> constexpr bool is_zero(T a) noexcept { return nem::abs(a) < eps<T>(); }
49 template <std::integral T> constexpr bool is_zero(T a) noexcept { return a == 0; }
50
51 template <nem::scalar_type T> constexpr bool is_zero_or_neg(T a) noexcept { return a <= eps<T>(); }
52
56 template <nem::scalar_type T> constexpr bool equal(T a, T b) noexcept { return nem::is_zero(a - b); }
57
58 template <nem::scalar_type T> constexpr T get_max(T a, T b) noexcept { return (a > b) ? a : b; }
59
60 template <nem::scalar_type T> constexpr T get_min(T a, T b) noexcept { return (a < b) ? a : b; }
61
62 template <nem::scalar_type T> constexpr T average(T a, T b) noexcept { return (a + b) / (T)2.0; }
63
64
68
69 template <typename T> constexpr T pow2(T value) noexcept { return T{ value * value }; }
70 template <typename T> constexpr T pow3(T value) noexcept { return T{ value * value * value }; }
71 template <typename T> constexpr T pow4(T value) noexcept { return T{ value * value * value * value }; }
72 template <typename T> constexpr T sqr (T value) noexcept { return T{ value * value }; }
73 template <typename T> constexpr T cube(T value) noexcept { return T{ value * value * value }; }
74
78
79 template <nem::scalar_type T> constexpr T truncate(T value) noexcept
80 {
81 return (T)(long long int)value;
82 }
83
87 template <nem::scalar_type T> constexpr T floor(T value) noexcept
88 {
89 const T trunc = nem::truncate(value);
90 return trunc - (trunc > value); // subtract 1 if truncation made the number bigger, which can happen only when the value is negative AND not-whole. If it is whole, then truncation never changes the number. If the number is positive and not-whole,
91 // | sign(value) | is whole? | sign(trunc - value) |
92 // | + | yes | 0 | --> 2.0 -> 2.0
93 // | + | no | -1 | --> 2.3 -> 2.0
94 // | - | yes | 0 | --> -2.0 -> -2.0
95 // | - | no | +1 | --> -2.3 -> -3.0
96 }
97
101 template <nem::scalar_type T> constexpr T ceil(T value) noexcept
102 {
103 const T floored = nem::floor(value);
104 return floored + (floored < value);
105 // | sign(value) | is whole? | sign(trunc - value) |
106 // | + | yes | 0 | --> 2.0 -> 2.0
107 // | + | no | -1 | --> 2.3 -> 3.0
108 // | - | yes | 0 | --> -2.0 -> -2.0
109 // | - | no | +1 | --> -2.3 -> -2.0
110 }
111
115 template <nem::scalar_type T> constexpr T mod(T a, T b)
116 {
117 if (nem::is_zero(b))
118 {
120 }
121 const T absb = nem::abs(b);
122 const T v = a - nem::floor(a / b) * b;
123 const T r = v < 0 ? v + absb : v;
124 return nem::equal(r, absb) ? (T)0.0 : r;
125 }
126
133 template <std::floating_point T> constexpr T frac(T value) noexcept
134 {
135 return value - nem::truncate(value);
136 }
137
144 template <std::floating_point T> constexpr T round(T value) noexcept
145 {
146 const T floored = nem::floor(value);
147 const T ceiled = floored + (floored < value); // ceil without extra floor
148 const T neg_dist = value - floored;
149 const T pos_dist = ceiled - value;
150 const bool up = pos_dist < (neg_dist + nem::eps<T>()); // 0.5 -> up
151 return ceiled * up + floored * !up; // branchless
152 }
153
157
165 template <std::floating_point T> constexpr T floor(T value, T step)
166 {
167 if (nem::is_zero_or_neg(step))
168 {
169 return nem::error::invalid_result<T>(nem::error::Kind::DivisionByZero, "Flooring only supports positive step values");
170 }
171 return nem::floor(value / step) * step;
172 }
173
177 template <std::integral T> constexpr T floor(T value, T step) noexcept
178 {
179 return (T)(nem::floor<nem::real>(static_cast<nem::real>(value), static_cast<nem::real>(step)));
180 }
181
189 template <std::floating_point T> constexpr T ceil(T value, T step)
190 {
191 if (nem::is_zero_or_neg(step))
192 {
193 return nem::error::invalid_result<T>(nem::error::Kind::DivisionByZero, "Ceiling only supports positive step values");
194 }
195 return nem::ceil(value / step) * step;
196 }
197
201 template <std::integral T> constexpr T ceil(T value, T step)
202 {
203 return (T)(nem::ceil<nem::real>(static_cast<nem::real>(value), static_cast<nem::real>(step)));
204 }
205
213 template <std::floating_point T> constexpr T round(T value, T step)
214 {
215 if (nem::is_zero_or_neg(step))
216 {
217 return nem::error::invalid_result<T>(nem::error::Kind::DivisionByZero, "Rounding only supports positive step values");
218 }
219 return nem::round(value / step) * step;
220 }
221
225 template <std::integral T> constexpr T round(T value, T step)
226 {
227 return (T)(nem::round<nem::real>(static_cast<nem::real>(value), static_cast<nem::real>(step)) + nem::eps<nem::real>());
228 }
229
233
237 template <nem::scalar_type T> constexpr T repeat(T value, T lengthExcl)
238 {
239 return nem::mod(value, lengthExcl);
240 }
241
245 template <nem::scalar_type T> constexpr T repeat(T value, T minIncl, T maxExcl)
246 {
247 return minIncl + nem::mod(value - minIncl, maxExcl - minIncl);
248 }
249
250 template <nem::scalar_type T> constexpr T pingpong(T value, T length)
251 {
252 const T t = nem::repeat(value, length * (T)2.0);
253 return length - nem::abs(t - length);
254 }
255
256 template <nem::scalar_type T> constexpr T pingpong(T value, T minIncl, T maxExcl)
257 {
258 return minIncl + nem::pingpong(value - minIncl, maxExcl - minIncl);
259 }
260
261 template <std::totally_ordered T> constexpr T clamp(T value, T minIncl, T maxIncl) noexcept
262 {
263 if (value > maxIncl) return maxIncl;
264 else if (value < minIncl) return minIncl;
265 return value;
266 }
267 template <std::totally_ordered T> constexpr T clamp01(T value) noexcept
268 {
269 return nem::clamp(value, (T)0.0, (T)1.0);
270 }
271
275
276 template <typename T> constexpr T smoothstep(T edge0, T edge1, T x)
277 {
278 const T length = edge1 - edge0;
279 if (nem::is_zero(length))
280 {
281 return nem::error::invalid_result<T>(nem::error::Kind::DivisionByZero, "Smoothstep edges are too closeby");
282 }
283 x = nem::clamp((x - edge0) / (edge1 - edge0), (T)0.0, (T)1.0);
284 return x * x * ((T)3.0 - (T)2.0 * x);
285 }
286 template <typename T> constexpr T lerp(T a, T b, T t) { return b * t + a * ((T)1.0 - t); }
287
291
292 template <nem::scalar_type T> constexpr T remap(T value, T fromMin, T fromMax, T toMin, T toMax)
293 {
294 const T fromLength = fromMax - fromMin;
295 if (nem::is_zero(fromLength))
296 {
297 return nem::error::invalid_result<T>(nem::error::Kind::DivisionByZero, "Remapping from length is zero");
298 }
299 return toMin + (toMax - toMin) * ((value - fromMin) / fromLength);
300 }
301
305
307 template <std::floating_point T> constexpr T is_whole(T value)
308 {
309 return nem::is_zero(nem::frac(value));
310 }
311
313 template <std::integral T> constexpr T is_whole(T value)
314 {
315 return true;
316 }
317}
T invalid_result(nem::error::Kind kind=Kind::RuntimeError, const char *const msg=nullptr)
Definition err.hpp:120
bool _nem_isfinite(T x) noexcept
Definition config.hpp:16
constexpr T eps()
Definition config.hpp:90
constexpr T smoothstep(T edge0, T edge1, T x)
Definition utils.hpp:276
constexpr T floor(T value) noexcept
Floors always towards -infinity.
Definition utils.hpp:87
constexpr T get_max(T a, T b) noexcept
Definition utils.hpp:58
constexpr T get_min(T a, T b) noexcept
Definition utils.hpp:60
constexpr T mod(T a, T b)
Calculates euclidean modulo of a % b = r, where r belongs to [0, b). Unline standard C++ modulo opera...
Definition utils.hpp:115
constexpr T ceil(T value) noexcept
Ceils always towards +infinity.
Definition utils.hpp:101
constexpr T repeat(T value, T lengthExcl)
Unwinds number to 0 when goes past length. Lengh is excluded: when value == lengthExcl,...
Definition utils.hpp:237
constexpr T remap(T value, T fromMin, T fromMax, T toMin, T toMax)
Definition utils.hpp:292
constexpr T is_nan(T value) noexcept
Definition utils.hpp:20
constexpr T sign(T value) noexcept
Definition utils.hpp:36
constexpr T abs(T value) noexcept
Definition utils.hpp:38
float real
Definition config.hpp:55
constexpr bool is_zero(T a) noexcept
Is a nearly 0? Given an Epsilon > 0, within each all numbers are considered indistinguisable from 0,...
Definition utils.hpp:48
constexpr T pow3(T value) noexcept
Definition utils.hpp:70
constexpr T pow4(T value) noexcept
Definition utils.hpp:71
constexpr bool is_zero_or_neg(T a) noexcept
Definition utils.hpp:51
constexpr T frac(T value) noexcept
Returns the fractional part of the floating-point number, with respect to sign 3.5 -> 0....
Definition utils.hpp:133
constexpr T pow2(T value) noexcept
Definition utils.hpp:69
constexpr T round(T value) noexcept
Definition utils.hpp:144
constexpr T pingpong(T value, T length)
Definition utils.hpp:250
constexpr T is_infinite(T value) noexcept
Definition utils.hpp:19
constexpr T copysign(T to, T from) noexcept
Copies the sign of {from} over to {to}. If the signs were the same, it doesn't change.
Definition utils.hpp:31
constexpr T clamp01(T value) noexcept
Definition utils.hpp:267
constexpr T truncate(T value) noexcept
Definition utils.hpp:79
constexpr T sqr(T value) noexcept
Definition utils.hpp:72
constexpr bool equal(T a, T b) noexcept
Are a and b nearly equal? Given an Epsilon > 0, within each all numbers are considered indistinguisab...
Definition utils.hpp:56
T length(const nem::quat_t< T > &q)
constexpr T lerp(T a, T b, T t)
Definition utils.hpp:286
constexpr T clamp(T value, T minIncl, T maxIncl) noexcept
Definition utils.hpp:261
constexpr T cube(T value) noexcept
Definition utils.hpp:73
constexpr T is_whole(T value)
Is a floating point number whole (same as integer)
Definition utils.hpp:307
constexpr T average(T a, T b) noexcept
Definition utils.hpp:62