jaulib v1.5.0
Jau Support Library (C++, Java, ..)
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
33#include <jau/float_types.hpp>
34#include <jau/base_math.hpp>
35#include <jau/string_util.hpp>
36
37#include <jau/string_cfmt.hpp>
38#include <jau/type_concepts.hpp>
39
40namespace jau {
41 /** @defgroup Floats Float types and arithmetic
42 * Float types and arithmetic, see also and meta-group \ref Math
43 * @{
44 */
45
46 /// Alias for `π` or half-circle radians (180 degrees), i.e. std::numbers::pi_v<T>
47 template<std::floating_point T>
48 inline constexpr T PI = std::numbers::pi_v<T>;
49
50 /// Alias for `π/2` or right-angle radians (90 degrees), i.e. std::numbers::pi_v<T>/T(2)
51 template<std::floating_point T>
52 inline constexpr T PI_2 = std::numbers::pi_v<T>/T(2);
53
54 /// Alias for `π/4` or half right-angle radians (45 degrees), i.e. std::numbers::pi_v<T>/T(4)
55 template<std::floating_point T>
56 inline constexpr T PI_4 = std::numbers::pi_v<T>/T(4);
57
58 /// Alias for `1/π` or inverse of `π`, i.e. T(1)/std::numbers::pi_v<T> or std::numbers::inv_pi_v<T>
59 template<std::floating_point T>
60 inline constexpr T inv_PI = std::numbers::inv_pi_v<T>;
61
62 /// Alias for epsilon constant, i.e. std::numeric_limits<T>::epsilon()
63 template<std::floating_point T>
64 inline constexpr T EPSILON = std::numeric_limits<T>::epsilon();
65
66 /**
67 * Calculates the smallest floating point value approximation
68 * the given type T can represent, the machine epsilon of T.
69 * @tparam T a non integer float type
70 * @return machine epsilon of T
71 */
72 template<std::floating_point T>
73 T machineEpsilon() noexcept
74 {
75 const T one(1);
76 const T two(2);
77 T x = one, res;
78 do {
79 res = x;
80 } while (one + (x /= two) > one);
81 return res;
82 }
83
84 /** Returns true if the given value is less than epsilon, w/ epsilon > 0. */
85 template<std::floating_point T>
86 constexpr bool is_zero(const T& a, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
87 return std::abs(a) < epsilon;
88 }
89
90 /** Returns true if all given values a and b are less than epsilon, w/ epsilon > 0. */
91 template<std::floating_point T>
92 constexpr bool is_zero2f(const T& a, const T& b, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
93 return std::abs(a) < epsilon && std::abs(b) < epsilon;
94 }
95
96 /** Returns true if all given values a, b and c are less than epsilon, w/ epsilon > 0. */
97 template<std::floating_point T>
98 constexpr bool is_zero3f(const T& a, const T& b, const T& c, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
99 return std::abs(a) < epsilon && std::abs(b) < epsilon && std::abs(c) < epsilon;
100 }
101
102 /** Returns true if all given values a, b, c and d are less than epsilon, w/ epsilon > 0. */
103 template<std::floating_point T>
104 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 {
105 return std::abs(a) < epsilon && std::abs(b) < epsilon && std::abs(c) < epsilon && std::abs(d) < epsilon;
106 }
107
108 /**
109 * Returns true if the given value is zero,
110 * disregarding `epsilon` but considering `NaN`, `-Inf` and `+Inf`.
111 */
112 template<std::floating_point T>
113 constexpr bool is_zero_raw(const T& a) noexcept {
114 return ( bit_value(a) & ~float_iec559_sign_bit ) == 0;
115 }
116
117 /**
118 * Returns `-1`, `0` or `1` if `a` is less, equal or greater than `b`,
119 * disregarding epsilon but considering `NaN`, `-Inf` and `+Inf`.
120 *
121 * Implementation considers following corner cases:
122 * - NaN == NaN
123 * - +Inf == +Inf
124 * - -Inf == -Inf
125 * - NaN > 0
126 * - +Inf > -Inf
127 *
128 * @tparam T a non integer float type
129 * @param a value to compare
130 * @param b value to compare
131 */
132 template<std::floating_point T>
133 constexpr int compare(const T a, const T b) noexcept {
134 if( a < b ) {
135 return -1; // Neither is NaN, a is smaller
136 }
137 if( a > b ) {
138 return 1; // Neither is NaN, a is larger
139 }
140 // a == b: we compare the _signed_ int value
141 typedef typename jau::uint_bytes_t<sizeof(T)> T_uint;
142 typedef typename std::make_signed_t<T_uint> T_int;
143 const T_int a_bits = static_cast<T_int>( bit_value(a) );
144 const T_int b_bits = static_cast<T_int>( bit_value(b) );
145 if( a_bits == b_bits ) {
146 return 0; // Values are equal (Inf, Nan .. )
147 } else if( a_bits < b_bits ) {
148 return -1; // (-0.0, 0.0) or (!NaN, NaN)
149 } else {
150 return 1; // ( 0.0, -0.0) or ( NaN, !NaN)
151 }
152 }
153
154 /**
155 * Returns `-1`, `0` or `1` if `a` is less, equal or greater than `b`,
156 * considering epsilon and `NaN`, `-Inf` and `+Inf`.
157 *
158 * `epsilon` must be > 0.
159 *
160 * Implementation considers following corner cases:
161 * - NaN == NaN
162 * - +Inf == +Inf
163 * - -Inf == -Inf
164 * - NaN > 0
165 * - +Inf > -Inf
166 *
167 * @tparam T a non integer float type
168 * @param a value to compare
169 * @param b value to compare
170 * @param epsilon defaults to std::numeric_limits<T>::epsilon(), must be > 0
171 */
172 template<std::floating_point T>
173 constexpr int compare(const T a, const T b, const T epsilon) noexcept {
174 if( std::abs(a - b) < epsilon ) {
175 return 0;
176 } else {
177 return compare(a, b);
178 }
179 }
180
181 /**
182 * Returns true if both values are equal
183 * disregarding epsilon but considering `NaN`, `-Inf` and `+Inf`.
184 *
185 * Implementation considers following corner cases:
186 * - NaN == NaN
187 * - +Inf == +Inf
188 * - -Inf == -Inf
189 *
190 * @tparam T a non integer float type
191 * @param a value to compare
192 * @param b value to compare
193 */
194 template<std::floating_point T>
195 constexpr bool equals_raw(const T& a, const T& b) noexcept {
196 // Values are equal (Inf, Nan .. )
197 return bit_value(a) == bit_value(b);
198 }
199
200 /**
201 * Returns true if both values are equal, i.e. their absolute delta < `epsilon`,
202 * considering epsilon and `NaN`, `-Inf` and `+Inf`.
203 *
204 * `epsilon` must be > 0.
205 *
206 * Implementation considers following corner cases:
207 * - NaN == NaN
208 * - +Inf == +Inf
209 * - -Inf == -Inf
210 *
211 * @tparam T a non integer float type
212 * @param a value to compare
213 * @param b value to compare
214 * @param epsilon defaults to std::numeric_limits<T>::epsilon(), must be > 0
215 */
216 template<std::floating_point T>
217 constexpr bool equals(const T& a, const T& b, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
218 if( std::abs(a - b) < epsilon ) {
219 return true;
220 } else {
221 // Values are equal (Inf, Nan .. )
222 return bit_value(a) == bit_value(b);
223 }
224 }
225
226 /**
227 * Returns true if both values are equal, i.e. their absolute delta < `epsilon`,
228 * considering epsilon but disregarding `NaN`, `-Inf` and `+Inf`.
229 *
230 * @tparam T a non integer float type
231 * @param a value to compare
232 * @param b value to compare
233 */
234 template<std::floating_point T>
235 constexpr bool equals2(const T& a, const T& b, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
236 return std::abs(a - b) < epsilon;
237 }
238
239 /**
240 * Returns true, if both floating point values are equal
241 * in the sense that their potential difference is less or equal <code>epsilon * ulp</code>.
242 *
243 * `epsilon` must be > 0.
244 *
245 * Implementation considers following corner cases:
246 * - NaN == NaN
247 * - +Inf == +Inf
248 * - -Inf == -Inf
249 *
250 * @tparam T a non integer float type
251 * @param a value to compare
252 * @param b value to compare
253 * @param ulp desired precision in ULPs (units in the last place)
254 * @param epsilon the machine epsilon of type T, defaults to <code>std::numeric_limits<T>::epsilon()</code>
255 */
256 template<std::floating_point T>
257 constexpr bool equals(const T& a, const T& b, int ulp, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept {
258 return equals(a, b, epsilon * ulp);
259 }
260
261 /**
262 * Returns true, if both floating point values are equal
263 * in the sense that their potential difference is less or equal <code>epsilon * |a+b| * ulp</code>,
264 * where <code>|a+b|</code> scales epsilon to the magnitude of used values.
265 *
266 * `epsilon` must be > 0.
267 *
268 * Implementation considers following corner cases:
269 * - NaN == NaN
270 * - +Inf == +Inf
271 * - -Inf == -Inf
272 *
273 * @tparam T a non integer float type
274 * @param a value to compare
275 * @param b value to compare
276 * @param ulp desired precision in ULPs (units in the last place), defaults to 1
277 * @param epsilon the machine epsilon of type T, defaults to <code>std::numeric_limits<T>::epsilon()</code>
278 */
279 template<std::floating_point T>
280 constexpr bool almost_equal(const T& a, const T& b, int ulp=1, const T& epsilon=std::numeric_limits<T>::epsilon()) noexcept
281 {
282 const T diff = std::fabs(a-b);
283 if( ( diff <= epsilon * std::fabs(a+b) * ulp ) ||
284 ( diff < std::numeric_limits<T>::min() ) ) { // subnormal limit
285 return true;
286 } else {
287 // Values are equal (Inf, Nan .. )
288 return bit_value(a) == bit_value(b);
289 }
290 }
291
292 /** Returns the rounded value cast to signed int. */
293 template<std::floating_point T>
294 constexpr typename jau::sint_bytes_t<sizeof(T)> round_to_int(const T v) noexcept {
295 return static_cast<typename jau::sint_bytes_t<sizeof(T)>>( std::round(v) );
296 }
297
298 /** Returns the rounded value cast to unsigned int. */
299 template<std::floating_point T>
300 constexpr typename jau::uint_bytes_t<sizeof(T)> round_to_uint(const T v) noexcept {
301 return static_cast<typename jau::uint_bytes_t<sizeof(T)>>( std::round(v) );
302 }
303
304 /** Converts arc-degree to radians */
305 template<std::floating_point T>
306 constexpr T adeg_to_rad(const T arc_degree) noexcept {
307 return arc_degree * PI<T> / T(180.0);
308 }
309
310 /** Converts radians to arc-degree */
311 template<std::floating_point T>
312 constexpr T rad_to_adeg(const T rad) noexcept {
313 return rad * T(180.0) * inv_PI<T>;
314 }
315
316 /**
317 * Appends a row of floating points to the given string `sb`
318 * @param sb string buffer to appends to
319 * @param f format string for each float element, e.g. "%10.5f"
320 * @param a the float data of size rows x columns
321 * @param rows float data `a` size row factor
322 * @param columns float data `a` size column factor
323 * @param rowMajorOrder if true floats are laid out in row-major-order, otherwise column-major-order (OpenGL)
324 * @param row selected row of float data `a`
325 * @return given string buffer `sb` for chaining
326 */
327 template<std::floating_point T>
328 std::string& row_to_string(std::string& sb, const std::string_view f,
329 const T a[],
330 const jau::nsize_t rows, const jau::nsize_t columns,
331 const bool rowMajorOrder, const jau::nsize_t row) noexcept {
332 if(rowMajorOrder) {
333 for(jau::nsize_t c=0; c<columns; ++c) {
334 sb.append( jau::format_string(f, a[ row*columns + c ] ) );
335 sb.append(", ");
336 }
337 } else {
338 for(jau::nsize_t c=0; c<columns; ++c) {
339 sb.append( jau::format_string(f, a[ row + c*rows ] ) );
340 sb.append(", ");
341 }
342 }
343 return sb;
344 }
345
346 /**
347 * Appends a matrix of floating points to the given string `sb`
348 * @param sb string buffer to appends to
349 * @param rowPrefix prefix for each row
350 * @param f format string for each float element, e.g. "%10.5f"
351 * @param a the float data of size rows x columns
352 * @param rows float data `a` size row factor
353 * @param columns float data `a` size column factor
354 * @param rowMajorOrder if true floats are laid out in row-major-order, otherwise column-major-order (OpenGL)
355 * @return given string buffer `sb` for chaining
356 */
357 template<std::floating_point T>
358 std::string& mat_to_string(std::string& sb, const std::string& rowPrefix, const std::string_view f,
359 const T a[], const jau::nsize_t rows, const jau::nsize_t columns,
360 const bool rowMajorOrder) noexcept {
361 sb.append(rowPrefix).append("{\n");
362 for(jau::nsize_t i=0; i<rows; ++i) {
363 sb.append(rowPrefix).append(" ");
364 row_to_string(sb, f, a, rows, columns, rowMajorOrder, i);
365 sb.append("\n");
366 }
367 sb.append(rowPrefix).append("}").append("\n");
368 return sb;
369 }
370
371 /**@}*/
372
373} // namespace jau
374
375#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:65
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:85
typename uint_bytes< bytesize >::type uint_bytes_t
Alias template for uint_bytes.
Definition int_types.hpp:55
__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)