JPL Spatial
Sound spatialization and propagation library
Loading...
Searching...
No Matches
Math.h
Go to the documentation of this file.
1//
2// ██╗██████╗ ██╗ ██╗██████╗ ███████╗
3// ██║██╔══██╗ ██║ ██║██╔══██╗██╔════╝ ** JPLSpatial **
4// ██║██████╔╝ ██║ ██║██████╔╝███████╗
5// ██ ██║██╔═══╝ ██║ ██║██╔══██╗╚════██║ https://github.com/Jaytheway/JPLSpatial
6// ╚█████╔╝██║ ███████╗██║██████╔╝███████║
7// ╚════╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚══════╝
8//
9// Copyright 2024 Jaroslav Pevno, JPLSpatial is offered under the terms of the ISC license:
10//
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or
12// without fee is hereby granted, provided that the above copyright notice and this permission
13// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
14// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
16// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
17// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20#pragma once
21
22#include "JPLSpatial/Core.h"
23
24#include <numbers>
25#include <cmath>
26#include <limits>
27#include <concepts>
28#include <type_traits>
29#include <bit>
30#include <concepts>
31#include <utility>
32
33namespace JPL
34{
35 static constexpr float JPL_HALF_PI = 0.5f * std::numbers::pi_v<float>;
36 static constexpr float JPL_PI = std::numbers::pi_v<float>;
37 static constexpr float JPL_TWO_PI = 2.0f * std::numbers::pi_v<float>;
38 static constexpr float JPL_FOUR_PI = 4.0f * std::numbers::pi_v<float>;
39
40 static constexpr float JPL_INV_PI = std::numbers::inv_pi_v<float>;
41 static constexpr float JPL_INV_TWO_PI = std::numbers::inv_pi_v<float> * 0.5f;
42 static constexpr float JPL_INV_HALF_PI = std::numbers::inv_pi_v<float> * 2.0f;
43
44 template<std::floating_point T> static constexpr T JPL_TO_RAD_V = std::numbers::pi_v<T> / T(180.0);
45 template<std::floating_point T> static constexpr T JPL_TO_DEG_V = T(180.0) / std::numbers::pi_v<T>;
46
47 static constexpr float JPL_TO_RAD = JPL_TO_RAD_V<float>;
48 static constexpr float JPL_TO_DEG = JPL_TO_DEG_V<float>;
49
50 template<std::floating_point T> struct FloatNubmer;
51 template<> struct FloatNubmer<float> { static constexpr float Tolerance = 1e-6f; };
52 template<> struct FloatNubmer<double> { static constexpr double Tolerance = 1e-8; };
53
54 template<std::floating_point T>
55 static constexpr float JPL_FLOAT_EPS_V = FloatNubmer<T>::Tolerance;
56
57 static constexpr float JPL_FLOAT_EPS = JPL_FLOAT_EPS_V<float>;
58}
59
60namespace JPL::Math
61{
62 template<class T> concept CArithmetic = std::is_arithmetic_v<T>;
63 template<class T1, class T2> concept COrderedWith = std::totally_ordered_with<T1, T2>;
64 template<class T1, class T2> concept CArithmeticAndOrderedWith = CArithmetic<T1> && COrderedWith<T1, T2>;
65
66 //======================================================================
68 template<std::floating_point T>
69 [[nodiscard]] JPL_INLINE constexpr T ToRadians(T degrees) noexcept
70 {
71 return degrees * JPL_TO_RAD_V<T>;
72 }
73
75 template<std::floating_point T>
76 [[nodiscard]] JPL_INLINE constexpr T ToDegrees(T radians) noexcept
77 {
78 return radians * JPL_TO_DEG_V<T>;
79 }
80
81 //======================================================================
82 template<std::floating_point T>
83 [[nodiscard]] JPL_INLINE constexpr bool IsNan(T x) noexcept { return x != x; }
84
86 template<CArithmetic T>
87 [[nodiscard]] JPL_INLINE constexpr auto Abs(const T& value) noexcept
88 {
89 return value < 0 ? -value : value;
90 }
91
92 //======================================================================
95 template<std::floating_point T>
96 [[nodiscard]] JPL_INLINE constexpr T Floor(T value) noexcept
97 {
98 if (std::is_constant_evaluated())
99 {
100 const T truncated_f = static_cast<T>(static_cast<int64>(value));
101 return truncated_f - static_cast<T>(value < truncated_f);
102 }
103 else
104 {
105 return std::floor(value);
106 }
107 }
108#if 0
109 static_assert(Floor(3.14) == 3.0);
110 static_assert(Floor(3.0) == 3.0);
111 static_assert(Floor(-3.14) == -4.0);
112 static_assert(Floor(-3.0) == -3.0);
113 static_assert(Floor(0.0) == 0.0);
114 static_assert(Floor(-0.5) == -1.0);
115#endif
116
118 template<std::integral T>
119 [[nodiscard]] JPL_INLINE constexpr T Floor(T value) noexcept { return value; }
120
121 //======================================================================
123 template<CArithmetic T>
124 [[nodiscard]] JPL_INLINE constexpr T Sign(T value) noexcept
125 {
126 return (value > T(0)) ? T(1) : ((value < T(0)) ? T(-1) : T(0));
127 }
128
130 template<CArithmetic T>
131 [[nodiscard]] JPL_INLINE constexpr T Sign2(T value) noexcept
132 {
133 if (std::is_constant_evaluated())
134 return value < T(0) ? T(-1) : T(1);
135 else
136 return std::copysign(T(1), value);
137 }
138
139 template<CArithmetic T1, CArithmeticAndOrderedWith<T1> T2>
140 [[nodiscard]] JPL_INLINE constexpr bool IsPositiveAndBelow(T1 value, T2 below) noexcept
141 {
142 return value > T1(0) && value < below;
143 }
144
145 template<std::floating_point T>
147 {
148 return Abs(value) <= errorTolerance;
149 }
150
151 template<std::floating_point T>
152 [[nodiscard]] JPL_INLINE constexpr bool IsNearlyEqual(T a, T b, T tolerance = JPL_FLOAT_EPS_V<T>) noexcept
153 {
154 return Abs(a - b) <= tolerance;
155 }
156
157 template<std::integral T>
158 [[nodiscard]] JPL_INLINE constexpr bool IsEven(T number) noexcept
159 {
160 return (number & T(1)) == 0;
161 }
162
163 template<std::floating_point T>
164 [[nodiscard]] JPL_INLINE std::pair<T, T> SinCos(T value) noexcept
165 {
166 // One day we may get std::sincos
167 return { std::sin(value), std::cos(value) };
168 }
169
171 template<std::floating_point T>
172 [[nodiscard]] JPL_INLINE constexpr T Lerp(const T& v0, const T& v1, T t) noexcept
173 {
174 return t * (v1 - v0) + v0;
175 }
176
178 template<std::integral T, std::floating_point Tt>
179 [[nodiscard]] JPL_INLINE constexpr T Lerp(const T& v0, const T& v1, Tt t) noexcept
180 {
181 return t * (v1 - v0) + v0;
182 }
183
185 template<std::floating_point T>
186 [[nodiscard]] JPL_INLINE constexpr T FMA(T a, T b, T c) noexcept
187 {
188 return a * b + c;
189 }
190
191 //======================================================================
193
194 namespace Detail
195 {
197 template <std::floating_point T>
198 [[nodiscard]] constexpr T QNan() noexcept
199 {
200 if constexpr (!std::numeric_limits<T>::is_iec559)
201 return std::numeric_limits<T>::quiet_NaN();
202 else if constexpr (std::is_same_v<T, float>)
203 return std::bit_cast<float>(0x7fc00000u);
204 else if constexpr (std::is_same_v<T, double>)
205 return std::bit_cast<double>(0x7ff8000000000000ull);
206 else
207 return std::numeric_limits<T>::quiet_NaN();
208 }
209
212 template <std::floating_point T>
213 [[nodiscard]] constexpr T InvSqrtInitialGuess(T x) noexcept
214 {
215 if constexpr (std::is_same_v<T, float>)
216 {
217 uint32_t i = std::bit_cast<uint32_t>(x);
218 i = 0x5f3759dfu - (i >> 1);
219 return std::bit_cast<float>(i);
220 }
221 else if constexpr (std::is_same_v<T, double>)
222 {
223 uint64_t i = std::bit_cast<uint64_t>(x);
224 i = 0x5fe6eb50c7b537a9ull - (i >> 1);
225 return std::bit_cast<double>(i);
226 }
227 else
228 {
229 // Safe positive seed; NR converges.
230 return T(1);
231 }
232 }
233
235 template <std::floating_point T, int NR>
236 [[nodiscard]] constexpr T InvSqrtNewtonRefine(T x, T y) noexcept
237 {
238 const T halfx = T(0.5) * x;
239 for (int i = 0; i < NR; ++i)
240 y = y * (T(1.5) - halfx * y * y);
241 return y;
242 }
243
245 template <std::floating_point T, int NR>
246 [[nodiscard]] constexpr T SqrtConstevalFallback(T x) noexcept
247 {
248 if (x == T(0)) return T(0);
249 if (x < T(0)) return QNan<T>();
250 if (x == std::numeric_limits<T>::infinity()) return x;
251 if (IsNan(x)) return x;
252
255 T r = x * y;
256
257 // Optional polish for float-only constexpr if you need tighter equality on tidy values:
258 if constexpr (std::is_same_v<T, float>)
259 r = T(0.5) * (r + x / r); // One Heron step (compile-time cost only)
260
261 return r;
262 }
263 } // namespace Detail
264
268 template<std::floating_point T, int NR = 2>
269 [[nodiscard]] JPL_INLINE constexpr T Sqrt(T x) noexcept
270 {
271 static_assert(NR >= 0, "NR must be >= 0");
272
273 if (std::is_constant_evaluated())
274 return Detail::SqrtConstevalFallback<T, NR>(x);
275 else
276 return std::sqrt(x);
277 }
278
282 template<std::floating_point T, int NR = 2>
283 [[nodiscard]] JPL_INLINE constexpr T InvSqrt(T x) noexcept
284 {
285 if (std::is_constant_evaluated())
286 return T(1.0) / Detail::SqrtConstevalFallback<T, NR>(x);
287 else
288 return T(1.0) / std::sqrt(x); // compiled down to rsqrtss, no division
289 }
290} // namespace JPL::Math
Definition Math.h:62
Definition Math.h:63
constexpr T SqrtConstevalFallback(T x) noexcept
Pure constexpr fallback sqrt using inverse-sqrt NR.
Definition Math.h:246
constexpr T InvSqrtInitialGuess(T x) noexcept
Definition Math.h:213
constexpr T QNan() noexcept
constexpr-friendly quiet NaN (IEEE-754 only)
Definition Math.h:198
constexpr T InvSqrtNewtonRefine(T x, T y) noexcept
Scalar NR on inverse sqrt.
Definition Math.h:236
Definition Math.h:61
JPL_INLINE constexpr T Floor(T value) noexcept
A constexpr implementation of floor.
Definition Math.h:96
JPL_INLINE constexpr T Sign(T value) noexcept
Sign returns -1 for negative values, 1 for positive, 0 for 0.
Definition Math.h:124
JPL_INLINE constexpr T FMA(T a, T b, T c) noexcept
Inlined fuse multiply-add. Compiler in some circumstances is more eager to optimize this than std::fm...
Definition Math.h:186
JPL_INLINE constexpr T ToRadians(T degrees) noexcept
Function to convert degrees to radians.
Definition Math.h:69
JPL_INLINE constexpr bool IsNan(T x) noexcept
Definition Math.h:83
JPL_INLINE constexpr T InvSqrt(T x) noexcept
Definition Math.h:283
JPL_INLINE constexpr T Lerp(const T &v0, const T &v1, T t) noexcept
Linearly interpolate v0 towards v1.
Definition Math.h:172
JPL_INLINE constexpr T Sqrt(T x) noexcept
Definition Math.h:269
JPL_INLINE constexpr bool IsNearlyZero(T value, T errorTolerance=JPL_FLOAT_EPS_V< T >) noexcept
Definition Math.h:146
JPL_INLINE constexpr bool IsNearlyEqual(T a, T b, T tolerance=JPL_FLOAT_EPS_V< T >) noexcept
Definition Math.h:152
JPL_INLINE constexpr bool IsPositiveAndBelow(T1 value, T2 below) noexcept
Definition Math.h:140
JPL_INLINE constexpr auto Abs(const T &value) noexcept
Standard abs is not constexpr in C++20.
Definition Math.h:87
JPL_INLINE std::pair< T, T > SinCos(T value) noexcept
Definition Math.h:164
JPL_INLINE constexpr T ToDegrees(T radians) noexcept
Function to convert radians to degrees.
Definition Math.h:76
JPL_INLINE constexpr T Sign2(T value) noexcept
Sign2 returns -1 for negative values, 1 otherwise.
Definition Math.h:131
JPL_INLINE constexpr bool IsEven(T number) noexcept
Definition Math.h:158
Definition AcousticMaterial.h:36
std::int64_t int64
Definition Core.h:317
JPL_INLINE simd min(const simd &a, const simd &b) noexcept
Element-wise min.
Definition SIMD.h:1813
Definition Math.h:50