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