Gamp v0.0.8
Gamp: Graphics, Audio, Multimedia and Processing
Loading...
Searching...
No Matches
float_math.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020-2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef JAU_FLOAT_MATH_HPP_
26#define JAU_FLOAT_MATH_HPP_
27
28#include <cmath>
29#include <climits>
30#include <concepts>
31#include <type_traits>
32#include <numbers>
33
34#include <jau/float_types.hpp>
35#include <jau/base_math.hpp>
36#include <jau/string_util.hpp>
37
38#include <jau/string_cfmt.hpp>
39#include <jau/type_concepts.hpp>
40
41namespace jau {
42 /** @defgroup Floats Float types and arithmetic
43 * Float types and arithmetic, see also and meta-group \ref Math
44 * @{
45 */
46
47 /// Alias for `π` or half-circle radians (180 degrees), i.e. std::numbers::pi_v<T>
48 template<std::floating_point T>
49 inline constexpr T PI = std::numbers::pi_v<T>;
50
51 /// Alias for `π/2` or right-angle radians (90 degrees), i.e. std::numbers::pi_v<T>/T(2)
52 template<std::floating_point T>
53 inline constexpr T PI_2 = std::numbers::pi_v<T>/T(2);
54
55 /// Alias for `π/4` or half right-angle radians (45 degrees), i.e. std::numbers::pi_v<T>/T(4)
56 template<std::floating_point T>
57 inline constexpr T PI_4 = std::numbers::pi_v<T>/T(4);
58
59 /// Alias for `1/π` or inverse of `π`, i.e. T(1)/std::numbers::pi_v<T> or std::numbers::inv_pi_v<T>
60 template<std::floating_point T>
61 inline constexpr T inv_PI = std::numbers::inv_pi_v<T>;
62
63 /// Alias for epsilon constant, i.e. std::numeric_limits<T>::epsilon()
64 template<std::floating_point T>
65 inline constexpr T EPSILON = std::numeric_limits<T>::epsilon();
66
67 /**
68 * Calculates the smallest floating point value approximation
69 * the given type T can represent, the machine epsilon of T.
70 * @tparam T a non integer float type
71 * @return machine epsilon of T
72 */
73 template<std::floating_point T>
74 T machineEpsilon() noexcept
75 {
76 const T one(1);
77 const T two(2);
78 T x = one, res;
79 do {
80 res = x;
81 } while (one + (x /= two) > one);
82 return res;
83 }
84
85 /** Returns true if the given value is less than epsilon, w/ epsilon > 0. */
86 template<std::floating_point T>
87 constexpr bool is_zero(const T& a, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
88 return std::abs(a) < epsilon;
89 }
90
91 /** Returns true if all given values a and b are less than epsilon, w/ epsilon > 0. */
92 template<std::floating_point T>
93 constexpr bool is_zero2f(const T& a, const T& b, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
94 return std::abs(a) < epsilon && std::abs(b) < epsilon;
95 }
96
97 /** Returns true if all given values a, b and c are less than epsilon, w/ epsilon > 0. */
98 template<std::floating_point T>
99 constexpr bool is_zero3f(const T& a, const T& b, const T& c, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
100 return std::abs(a) < epsilon && std::abs(b) < epsilon && std::abs(c) < epsilon;
101 }
102
103 /** Returns true if all given values a, b, c and d are less than epsilon, w/ epsilon > 0. */
104 template<std::floating_point T>
105 constexpr bool is_zero4f(const T& a, const T& b, const T& c, const T& d, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
106 return std::abs(a) < epsilon && std::abs(b) < epsilon && std::abs(c) < epsilon && std::abs(d) < epsilon;
107 }
108
109 /**
110 * Returns true if the given value is zero,
111 * disregarding `epsilon` but considering `NaN`, `-Inf` and `+Inf`.
112 */
113 template<std::floating_point T>
114 constexpr bool is_zero_raw(const T& a) noexcept {
115 return ( bit_value(a) & ~float_iec559_sign_bit ) == 0;
116 }
117
118 /**
119 * Returns `-1`, `0` or `1` if `a` is less, equal or greater than `b`,
120 * disregarding epsilon but considering `NaN`, `-Inf` and `+Inf`.
121 *
122 * Implementation considers following corner cases:
123 * - NaN == NaN
124 * - +Inf == +Inf
125 * - -Inf == -Inf
126 * - NaN > 0
127 * - +Inf > -Inf
128 *
129 * @tparam T a non integer float type
130 * @param a value to compare
131 * @param b value to compare
132 */
133 template<std::floating_point T>
134 constexpr int compare(const T a, const T b) noexcept {
135 if( a < b ) {
136 return -1; // Neither is NaN, a is smaller
137 }
138 if( a > b ) {
139 return 1; // Neither is NaN, a is larger
140 }
141 // a == b: we compare the _signed_ int value
142 typedef typename jau::uint_bytes_t<sizeof(T)> T_uint;
143 typedef typename std::make_signed_t<T_uint> T_int;
144 const T_int a_bits = static_cast<T_int>( bit_value(a) );
145 const T_int b_bits = static_cast<T_int>( bit_value(b) );
146 if( a_bits == b_bits ) {
147 return 0; // Values are equal (Inf, Nan .. )
148 } else if( a_bits < b_bits ) {
149 return -1; // (-0.0, 0.0) or (!NaN, NaN)
150 } else {
151 return 1; // ( 0.0, -0.0) or ( NaN, !NaN)
152 }
153 }
154
155 /**
156 * Returns `-1`, `0` or `1` if `a` is less, equal or greater than `b`,
157 * considering epsilon and `NaN`, `-Inf` and `+Inf`.
158 *
159 * `epsilon` must be > 0.
160 *
161 * Implementation considers following corner cases:
162 * - NaN == NaN
163 * - +Inf == +Inf
164 * - -Inf == -Inf
165 * - NaN > 0
166 * - +Inf > -Inf
167 *
168 * @tparam T a non integer float type
169 * @param a value to compare
170 * @param b value to compare
171 * @param epsilon defaults to std::numeric_limits<T>::epsilon(), must be > 0
172 */
173 template<std::floating_point T>
174 constexpr int compare(const T a, const T b, const T epsilon) noexcept {
175 if( std::abs(a - b) < epsilon ) {
176 return 0;
177 } else {
178 return compare(a, b);
179 }
180 }
181
182 /**
183 * Returns true if both values are equal
184 * disregarding epsilon but considering `NaN`, `-Inf` and `+Inf`.
185 *
186 * Implementation considers following corner cases:
187 * - NaN == NaN
188 * - +Inf == +Inf
189 * - -Inf == -Inf
190 *
191 * @tparam T a non integer float type
192 * @param a value to compare
193 * @param b value to compare
194 */
195 template<std::floating_point T>
196 constexpr bool equals_raw(const T& a, const T& b) noexcept {
197 // Values are equal (Inf, Nan .. )
198 return bit_value(a) == bit_value(b);
199 }
200
201 /**
202 * Returns true if both values are equal, i.e. their absolute delta < `epsilon`,
203 * considering epsilon and `NaN`, `-Inf` and `+Inf`.
204 *
205 * `epsilon` must be > 0.
206 *
207 * Implementation considers following corner cases:
208 * - NaN == NaN
209 * - +Inf == +Inf
210 * - -Inf == -Inf
211 *
212 * @tparam T a non integer float type
213 * @param a value to compare
214 * @param b value to compare
215 * @param epsilon defaults to std::numeric_limits<T>::epsilon(), must be > 0
216 */
217 template<std::floating_point T>
218 constexpr bool equals(const T& a, const T& b, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
219 if( std::abs(a - b) < epsilon ) {
220 return true;
221 } else {
222 // Values are equal (Inf, Nan .. )
223 return bit_value(a) == bit_value(b);
224 }
225 }
226
227 /**
228 * Returns true if both values are equal, i.e. their absolute delta < `epsilon`,
229 * considering epsilon but disregarding `NaN`, `-Inf` and `+Inf`.
230 *
231 * @tparam T a non integer float type
232 * @param a value to compare
233 * @param b value to compare
234 */
235 template<std::floating_point T>
236 constexpr bool equals2(const T& a, const T& b, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
237 return std::abs(a - b) < epsilon;
238 }
239
240 /**
241 * Returns true, if both floating point values are equal
242 * in the sense that their potential difference is less or equal <code>epsilon * ulp</code>.
243 *
244 * `epsilon` must be > 0.
245 *
246 * Implementation considers following corner cases:
247 * - NaN == NaN
248 * - +Inf == +Inf
249 * - -Inf == -Inf
250 *
251 * @tparam T a non integer float type
252 * @param a value to compare
253 * @param b value to compare
254 * @param ulp desired precision in ULPs (units in the last place)
255 * @param epsilon the machine epsilon of type T, defaults to <code>std::numeric_limits<T>::epsilon()</code>
256 */
257 template<std::floating_point T>
258 constexpr bool equals(const T& a, const T& b, int ulp, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
259 return equals(a, b, epsilon * ulp);
260 }
261
262 /**
263 * Returns true, if both floating point values are equal
264 * in the sense that their potential difference is less or equal <code>epsilon * |a+b| * ulp</code>,
265 * where <code>|a+b|</code> scales epsilon to the magnitude of used values.
266 *
267 * `epsilon` must be > 0.
268 *
269 * Implementation considers following corner cases:
270 * - NaN == NaN
271 * - +Inf == +Inf
272 * - -Inf == -Inf
273 *
274 * @tparam T a non integer float type
275 * @param a value to compare
276 * @param b value to compare
277 * @param ulp desired precision in ULPs (units in the last place), defaults to 1
278 * @param epsilon the machine epsilon of type T, defaults to <code>std::numeric_limits<T>::epsilon()</code>
279 */
280 template<std::floating_point T>
281 constexpr bool almost_equal(const T& a, const T& b, int ulp=1, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept
282 {
283 const T diff = std::fabs(a-b);
284 if( ( diff <= epsilon * std::fabs(a+b) * ulp ) ||
285 ( diff < std::numeric_limits<T>::min() ) ) { // subnormal limit
286 return true;
287 } else {
288 // Values are equal (Inf, Nan .. )
289 return bit_value(a) == bit_value(b);
290 }
291 }
292
293 /** Returns the rounded value cast to signed int. */
294 template<std::floating_point T>
295 constexpr typename jau::sint_bytes_t<sizeof(T)> round_to_int(const T v) noexcept {
296 return static_cast<typename jau::sint_bytes_t<sizeof(T)>>( std::round(v) );
297 }
298
299 /** Returns the rounded value cast to unsigned int. */
300 template<std::floating_point T>
301 constexpr typename jau::uint_bytes_t<sizeof(T)> round_to_uint(const T v) noexcept {
302 return static_cast<typename jau::uint_bytes_t<sizeof(T)>>( std::round(v) );
303 }
304
305 /** Converts arc-degree to radians */
306 template<std::floating_point T>
307 constexpr T adeg_to_rad(const T arc_degree) noexcept {
308 return arc_degree * PI<T> / T(180.0);
309 }
310
311 /** Converts radians to arc-degree */
312 template<std::floating_point T>
313 constexpr T rad_to_adeg(const T rad) noexcept {
314 return rad * T(180.0) * inv_PI<T>;
315 }
316
317 /**
318 * Appends a row of floating points to the given string `sb`
319 * @param sb string buffer to appends to
320 * @param f format string for each float element, e.g. "%10.5f"
321 * @param a the float data of size rows x columns
322 * @param rows float data `a` size row factor
323 * @param columns float data `a` size column factor
324 * @param rowMajorOrder if true floats are laid out in row-major-order, otherwise column-major-order (OpenGL)
325 * @param row selected row of float data `a`
326 * @return given string buffer `sb` for chaining
327 */
328 template<std::floating_point T>
329 std::string& row_to_string(std::string& sb, const std::string_view f,
330 const T a[],
331 const jau::nsize_t rows, const jau::nsize_t columns,
332 const bool rowMajorOrder, const jau::nsize_t row) noexcept {
333 if(rowMajorOrder) {
334 for(jau::nsize_t c=0; c<columns; ++c) {
335 sb.append( jau::format_string(f, a[ row*columns + c ] ) );
336 sb.append(", ");
337 }
338 } else {
339 for(jau::nsize_t c=0; c<columns; ++c) {
340 sb.append( jau::format_string(f, a[ row + c*rows ] ) );
341 sb.append(", ");
342 }
343 }
344 return sb;
345 }
346
347 /**
348 * Appends a matrix of floating points to the given string `sb`
349 * @param sb string buffer to appends to
350 * @param rowPrefix prefix for each row
351 * @param f format string for each float element, e.g. "%10.5f"
352 * @param a the float data of size rows x columns
353 * @param rows float data `a` size row factor
354 * @param columns float data `a` size column factor
355 * @param rowMajorOrder if true floats are laid out in row-major-order, otherwise column-major-order (OpenGL)
356 * @return given string buffer `sb` for chaining
357 */
358 template<std::floating_point T>
359 std::string& mat_to_string(std::string& sb, const std::string& rowPrefix, const std::string_view f,
360 const T a[], const jau::nsize_t rows, const jau::nsize_t columns,
361 const bool rowMajorOrder) noexcept {
362 sb.append(rowPrefix).append("{\n");
363 for(jau::nsize_t i=0; i<rows; ++i) {
364 sb.append(rowPrefix).append(" ");
365 row_to_string(sb, f, a, rows, columns, rowMajorOrder, i);
366 sb.append("\n");
367 }
368 sb.append(rowPrefix).append("}").append("\n");
369 return sb;
370 }
371
372 /**@}*/
373
374} // namespace jau
375
376#endif /* JAU_FLOAT_MATH_HPP_ */
constexpr T PI
Alias for π or half-circle radians (180 degrees), i.e. std::numbers::pi_v<T>
constexpr bool is_zero3f(const T &a, const T &b, const T &c, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if all given values a, b and c are less than epsilon, w/ epsilon > 0.
constexpr int compare(const T a, const T b) noexcept
Returns -1, 0 or 1 if a is less, equal or greater than b, disregarding epsilon but considering NaN,...
constexpr T rad_to_adeg(const T rad) noexcept
Converts radians to arc-degree.
constexpr bool almost_equal(const T &a, const T &b, int ulp=1, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true, if both floating point values are equal in the sense that their potential difference is...
constexpr jau::sint_bytes_t< sizeof(T)> round_to_int(const T v) noexcept
Returns the rounded value cast to signed int.
constexpr uint32_t bit_value(const float a) noexcept
Returns the unsigned integer representation according to IEEE 754 (IEC 559) single floating-point bit...
constexpr bool is_zero_raw(const T &a) noexcept
Returns true if the given value is zero, disregarding epsilon but considering NaN,...
constexpr T EPSILON
Alias for epsilon constant, i.e. std::numeric_limits<T>::epsilon()
constexpr bool is_zero2f(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if all given values a and b are less than epsilon, w/ epsilon > 0.
constexpr bool equals(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
constexpr T PI_4
Alias for π/4 or half right-angle radians (45 degrees), i.e. std::numbers::pi_v<T>/T(4)
constexpr bool equals_raw(const T &a, const T &b) noexcept
Returns true if both values are equal disregarding epsilon but considering NaN, -Inf and +Inf.
constexpr bool equals2(const T &a, const T &b, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if both values are equal, i.e.
constexpr T adeg_to_rad(const T arc_degree) noexcept
Converts arc-degree to radians.
std::string & row_to_string(std::string &sb, const std::string_view f, const T a[], const jau::nsize_t rows, const jau::nsize_t columns, const bool rowMajorOrder, const jau::nsize_t row) noexcept
Appends a row of floating points to the given string sb
constexpr uint32_t const float_iec559_sign_bit
Signed bit 31 of IEEE 754 (IEC 559) single float-point bit layout, i.e.
constexpr T PI_2
Alias for π/2 or right-angle radians (90 degrees), i.e. std::numbers::pi_v<T>/T(2)
constexpr bool is_zero4f(const T &a, const T &b, const T &c, const T &d, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if all given values a, b, c and d are less than epsilon, w/ epsilon > 0.
std::string & mat_to_string(std::string &sb, const std::string &rowPrefix, const std::string_view f, const T a[], const jau::nsize_t rows, const jau::nsize_t columns, const bool rowMajorOrder) noexcept
Appends a matrix of floating points to the given string sb
constexpr T inv_PI
Alias for 1/π or inverse of π, i.e. T(1)/std::numbers::pi_v<T> or std::numbers::inv_pi_v<T>
T machineEpsilon() noexcept
Calculates the smallest floating point value approximation the given type T can represent,...
constexpr jau::uint_bytes_t< sizeof(T)> round_to_uint(const T v) noexcept
Returns the rounded value cast to unsigned int.
constexpr bool is_zero(const T &a, const T &epsilon=std::numeric_limits< T >::epsilon()) noexcept
Returns true if the given value is less than epsilon, w/ epsilon > 0.
typename sint_bytes< bytesize >::type sint_bytes_t
Alias template for sint_bytes.
Definition int_types.hpp:69
uint_bytes_t< sizeof(unsigned long int)> nsize_t
Natural 'size_t' alternative using uint<XX>_t with xx = sizeof(unsigned long int)*8 as its natural si...
Definition int_types.hpp:89
typename uint_bytes< bytesize >::type uint_bytes_t
Alias template for uint_bytes.
Definition int_types.hpp:57
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
std::string format_string(std::string_view fmt, const Args &...args) noexcept
Safely returns a (non-truncated) string according to snprintf() formatting rules using a reserved str...
static std::string f(uint32_t v)