jaulib v1.3.6
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
int_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_INT_MATH_HPP_
26#define JAU_INT_MATH_HPP_
27
28#include <cstdint>
29#include <cmath>
30#include <climits>
31
32#include <jau/base_math.hpp>
33#include <jau/int_math_ct.hpp>
34
35namespace jau {
36
37 /** \addtogroup Integer
38 *
39 * @{
40 */
41
42 /**
43 * base_math: arithmetic types, i.e. integral + floating point types
44 * int_math: integral types
45 * float_math: floating point types
46 // *************************************************
47 // *************************************************
48 // *************************************************
49 */
50
51 // Remember: constexpr specifier used in a function or static data member (since C++17) declaration implies inline.
52
53 /** Returns true of the given integer value is zero. */
54 template<class T>
55 typename std::enable_if_t<std::is_integral_v<T>, bool>
56 constexpr is_zero(const T& a) noexcept {
57 return 0 == a;
58 }
59
60 /**
61 * Returns true if both values are equal.
62 *
63 * @tparam T an integral type
64 * @param a value to compare
65 * @param b value to compare
66 */
67 template<class T>
68 typename std::enable_if_t<std::is_integral_v<T>, bool>
69 constexpr equals(const T& a, const T& b) noexcept {
70 return a == b;
71 }
72
73 /**
74 * Returns true if both values are equal, i.e. their absolute delta <= `allowed_deviation`.
75 *
76 * @tparam T an integral type
77 * @param a value to compare
78 * @param b value to compare
79 * @param allowed_deviation allowed deviation
80 */
81 template<class T>
82 typename std::enable_if_t<std::is_integral_v<T>, bool>
83 constexpr equals(const T& a, const T& b, const T& allowed_deviation) noexcept {
84 return std::abs(a - b) <= allowed_deviation;
85 }
86
87 /**
88 * Round up w/ branching in O(1)
89 *
90 * @tparam T an unsigned integral number type
91 * @tparam U an unsigned integral number type
92 * @param n to be aligned number
93 * @param align_to alignment boundary, must not be 0
94 * @return n rounded up to a multiple of align_to
95 */
96 template <typename T, typename U,
97 std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T> &&
98 std::is_integral_v<U> && std::is_unsigned_v<U>, bool> = true>
99 constexpr T round_up(const T n, const U align_to) {
100 assert(align_to != 0); // align_to must not be 0
101
102 if(n % align_to) {
103 return n + ( align_to - ( n % align_to ) );
104 } else {
105 return n;
106 }
107 }
108
109 /**
110 * Round down w/ branching in O(1)
111 *
112 * @tparam T an unsigned integral number type
113 * @tparam U an unsigned integral number type
114 * @param n to be aligned number
115 * @param align_to alignment boundary
116 * @return n rounded down to a multiple of align_to
117 */
118 template <typename T, typename U,
119 std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T> &&
120 std::is_integral_v<U> && std::is_unsigned_v<U>, bool> = true>
121 constexpr T round_down(T n, U align_to) {
122 return align_to == 0 ? n : ( n - ( n % align_to ) );
123 }
124
125 /**
126 * Power of 2 test (w/o branching ?) in O(1)
127 *
128 * Source: [bithacks Test PowerOf2](http://www.graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2)
129 *
130 * Branching may occur due to relational operator.
131 *
132 * @tparam T an unsigned integral number type
133 * @param x the unsigned integral number
134 * @return true if arg is 2^n for some n > 0
135 */
136 template <typename T,
137 std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T>, bool> = true>
138 constexpr bool is_power_of_2(const T x) noexcept
139 {
140 return 0<x && 0 == ( x & static_cast<T>( x - 1 ) );
141 }
142
143 /**
144 * If the given {@code n} is not is_power_of_2() return next_power_of_2(),
145 * otherwise return {@code n} unchanged.
146 * <pre>
147 * return is_power_of_2(n) ? n : next_power_of_2(n);
148 * </pre>
149 */
150 constexpr uint32_t round_to_power_of_2(const uint32_t n) {
151 return is_power_of_2(n) ? n : ct_next_power_of_2(n);
152 }
153
154 /**
155 * Return the index of the highest set bit w/ branching (loop) in O(n), actually O(n/2).
156 *
157 * @tparam T an unsigned integral number type
158 * @param x value
159 */
160 template <typename T,
161 std::enable_if_t< std::is_integral_v<T> && std::is_unsigned_v<T>, bool> = true>
162 constexpr nsize_t high_bit(T x)
163 {
164 nsize_t hb = 0;
165 for(nsize_t s = ( CHAR_BIT * sizeof(T) ) >> 1; s > 0; s >>= 1) {
166 const nsize_t z = s * ( ( ~jau::ct_is_zero( x >> s ) ) & 1 );
167 hb += z;
168 x >>= z;
169 }
170 return hb + x;
171 }
172
173 /**
174 * Query whether `__builtin_add_overflow` is available
175 * via `__has_builtin(__builtin_add_overflow)`.
176 */
178 #if defined __has_builtin && __has_builtin(__builtin_add_overflow)
179 return true;
180 #else
181 return false;
182 #endif
183 }
184 /**
185 * Query whether `__builtin_sub_overflow` is available
186 * via `__has_builtin(__builtin_sub_overflow)`.
187 */
189 #if defined __has_builtin && __has_builtin(__builtin_sub_overflow)
190 return true;
191 #else
192 return false;
193 #endif
194 }
195 /**
196 * Query whether `__builtin_mul_overflow` is available
197 * via `__has_builtin(__builtin_mul_overflow)`.
198 */
200 #if defined __has_builtin && __has_builtin(__builtin_mul_overflow)
201 return true;
202 #else
203 return false;
204 #endif
205 }
206
207 /**
208 * Integer overflow aware addition returning true if overflow occurred,
209 * otherwise false having the result stored in res.
210 *
211 * Implementation uses [Integer Overflow Builtins](https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html)
212 * if available, otherwise its own implementation.
213 *
214 * @tparam T an integral integer type
215 * @tparam
216 * @param a operand a
217 * @param b operand b
218 * @param res storage for result
219 * @return true if overflow, otherwise false
220 */
221 template <typename T,
222 std::enable_if_t< std::is_integral_v<T>, bool> = true>
223 constexpr bool add_overflow(const T a, const T b, T& res) noexcept
224 {
225 if constexpr( has_builtin_add_overflow() ) {
226 return __builtin_add_overflow(a, b, &res);
227 } else {
228 // overflow: a + b > R+ -> a > R+ - b, with b >= 0
229 // underflow: a + b < R- -> a < R- - b, with b < 0
230 if ( ( b >= 0 && a > std::numeric_limits<T>::max() - b ) ||
231 ( b < 0 && a < std::numeric_limits<T>::min() - b ) )
232 {
233 return true;
234 } else {
235 res = a + b;
236 return false;
237 }
238 }
239 }
240
241 /**
242 * Integer overflow aware subtraction returning true if overflow occurred,
243 * otherwise false having the result stored in res.
244 *
245 * Implementation uses [Integer Overflow Builtins](https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html)
246 * if available, otherwise its own implementation.
247 *
248 * @tparam T an integral integer type
249 * @tparam
250 * @param a operand a
251 * @param b operand b
252 * @param res storage for result
253 * @return true if overflow, otherwise false
254 */
255 template <typename T,
256 std::enable_if_t< std::is_integral_v<T>, bool> = true>
257 constexpr bool sub_overflow(const T a, const T b, T& res) noexcept
258 {
259 if constexpr( has_builtin_sub_overflow() ) {
260 return __builtin_sub_overflow(a, b, &res);
261 } else {
262 // overflow: a - b > R+ -> a > R+ + b, with b < 0
263 // underflow: a - b < R- -> a < R- + b, with b >= 0
264 if ( ( b < 0 && a > std::numeric_limits<T>::max() + b ) ||
265 ( b >= 0 && a < std::numeric_limits<T>::min() + b ) )
266 {
267 return true;
268 } else {
269 res = a - b;
270 return false;
271 }
272 }
273 }
274
275 /**
276 * Integer overflow aware multiplication returning true if overflow occurred,
277 * otherwise false having the result stored in res.
278 *
279 * Implementation uses [Integer Overflow Builtins](https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html)
280 * if available, otherwise its own implementation.
281 *
282 * @tparam T an integral integer type
283 * @tparam
284 * @param a operand a
285 * @param b operand b
286 * @param res storage for result
287 * @return true if overflow, otherwise false
288 */
289 template <typename T,
290 std::enable_if_t< std::is_integral_v<T>, bool> = true>
291 constexpr bool mul_overflow(const T a, const T b, T& res) noexcept
292 {
293 if constexpr( has_builtin_mul_overflow() ) {
294 return __builtin_mul_overflow(a, b, &res);
295 } else {
296 // overflow: a * b > R+ -> a > R+ / b
297 if ( ( b > 0 && abs(a) > std::numeric_limits<T>::max() / b ) ||
298 ( b < 0 && abs(a) > std::numeric_limits<T>::min() / b ) )
299 {
300 return true;
301 } else {
302 res = a * b;
303 return false;
304 }
305 }
306 }
307
308 /**
309 * Returns the greatest common divisor (GCD) of the two given integer values following Euclid's algorithm from Euclid's Elements ~300 BC,
310 * using the absolute positive value of given integers.
311 *
312 * Returns zero if a and b is zero.
313 *
314 * Note implementation uses modulo operator `(a/b)*b + a % b = a `,
315 * i.e. remainder of the integer division - hence implementation uses `abs(a) % abs(b)`
316 * in case the integral T is a signed type (dropped for unsigned).
317 *
318 * Implementation is similar to std::gcd(), however, it uses a fixed common type T
319 * and a while loop instead of compile time evaluation via recursion.
320 *
321 * @tparam T integral type
322 * @tparam
323 * @param a integral value a
324 * @param b integral value b
325 * @return zero if a and b are zero, otherwise the greatest common divisor (GCD) of a and b,
326 */
327 template <typename T,
328 std::enable_if_t< std::is_integral_v<T> &&
329 !std::is_unsigned_v<T>, bool> = true>
330 constexpr T gcd(T a, T b) noexcept
331 {
332 T a_ = abs(a);
333 T b_ = abs(b);
334 while( b_ != 0 ) {
335 const T t = b_;
336 b_ = a_ % b_;
337 a_ = t;
338 }
339 return a_;
340 }
341
342 template <typename T,
343 std::enable_if_t< std::is_integral_v<T> &&
344 std::is_unsigned_v<T>, bool> = true>
345 constexpr T gcd(T a, T b) noexcept
346 {
347 while( b != 0 ) {
348 const T t = b;
349 b = a % b;
350 a = t;
351 }
352 return a;
353 }
354
355 /**
356 * Integer overflow aware calculation of least common multiple (LCM) following Euclid's algorithm from Euclid's Elements ~300 BC.
357 * @tparam T integral type
358 * @tparam
359 * @param result storage for lcm result: zero if a and b are zero, otherwise lcm of a and b
360 * @param a integral value a
361 * @param b integral value b
362 * @return true if overflow, otherwise false for success
363 */
364 template <typename T,
365 std::enable_if_t< std::is_integral_v<T>, bool> = true>
366 constexpr bool lcm_overflow(const T a, const T b, T& result) noexcept
367 {
368 const T _gcd = gcd<T>( a, b );
369 if( 0 < _gcd ) {
370 T r;
371 if( mul_overflow(a, b, r) ) {
372 return true;
373 } else {
374 result = r / _gcd;
375 return false;
376 }
377 } else {
378 result = 0;
379 return false;
380 }
381 }
382
383 /**
384 * Returns the number of decimal digits of the given integral value number using std::log10<T>().<br>
385 * If sign_is_digit == true (default), treats a potential negative sign as a digit.
386 * <pre>
387 * x < 0: 1 + (int) ( log10( -x ) ) + ( sign_is_digit ? 1 : 0 )
388 * x = 0: 1
389 * x > 0: 1 + (int) ( log10( x ) )
390 * </pre>
391 * Implementation uses jau::invert_sign() to have a safe absolute value conversion, if required.
392 * <p>
393 * Convenience method, reusing precomputed sign of value to avoid redundant computations.
394 * </p>
395 * @tparam T an integral integer type
396 * @param x the integral integer
397 * @param x_sign the pre-determined sign of the given value x
398 * @param sign_is_digit if true and value is negative, adds one to result for sign. Defaults to true.
399 * @return digit count
400 */
401 template <typename T,
402 std::enable_if_t< std::is_integral_v<T>, bool> = true>
403 constexpr nsize_t digits10(const T x, const snsize_t x_sign, const bool sign_is_digit=true) noexcept
404 {
405 if( x_sign == 0 ) {
406 return 1;
407 }
408 if( x_sign < 0 ) {
409 return 1 + static_cast<nsize_t>( std::log10<T>( invert_sign<T>( x ) ) ) + ( sign_is_digit ? 1 : 0 );
410 } else {
411 return 1 + static_cast<nsize_t>( std::log10<T>( x ) );
412 }
413 }
414
415 /**
416 * Returns the number of decimal digits of the given integral value number using std::log10<T>().
417 * If sign_is_digit == true (default), treats a potential negative sign as a digit.
418 * <pre>
419 * x < 0: 1 + (int) ( log10( -x ) ) + ( sign_is_digit ? 1 : 0 )
420 * x = 0: 1
421 * x > 0: 1 + (int) ( log10( x ) )
422 * </pre>
423 * Implementation uses jau::invert_sign() to have a safe absolute value conversion, if required.
424 * @tparam T an integral integer type
425 * @param x the integral integer
426 * @param sign_is_digit if true and value is negative, adds one to result for sign. Defaults to true.
427 * @return digit count
428 */
429 template <typename T,
430 std::enable_if_t< std::is_integral_v<T>, bool> = true>
431 constexpr nsize_t digits10(const T x, const bool sign_is_digit=true) noexcept
432 {
433 return digits10<T>(x, jau::sign<T>(x), sign_is_digit);
434 }
435
436 /**@}*/
437
438} // namespace jau
439
440#endif /* JAU_INT_MATH_HPP_ */
constexpr uint32_t ct_next_power_of_2(uint32_t n)
Returns the next higher power of 2 of given unsigned 32-bit n (w/o branching) in O(1) and constant ti...
constexpr T ct_is_zero(T x)
Returns ~0 (2-complement) if arg is zero, otherwise 0 (w/o branching) in O(1) and constant time (CT).
#define consteval_cxx20
consteval qualifier replacement for C++20 consteval.
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr 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.
std::enable_if_t< std::is_floating_point_v< T >, bool > constexpr 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.
constexpr T invert_sign(const T x) noexcept
Safely inverts the sign of an arithmetic number w/ branching in O(1)
constexpr T round_up(const T n, const U align_to)
Round up w/ branching in O(1)
Definition int_math.hpp:99
consteval_cxx20 bool has_builtin_mul_overflow() noexcept
Query whether __builtin_mul_overflow is available via __has_builtin(__builtin_mul_overflow).
Definition int_math.hpp:199
constexpr nsize_t digits10(const T x, const snsize_t x_sign, const bool sign_is_digit=true) noexcept
Returns the number of decimal digits of the given integral value number using std::log10<T>().
Definition int_math.hpp:403
constexpr nsize_t high_bit(T x)
Return the index of the highest set bit w/ branching (loop) in O(n), actually O(n/2).
Definition int_math.hpp:162
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition int_types.hpp:55
constexpr bool mul_overflow(const T a, const T b, T &res) noexcept
Integer overflow aware multiplication returning true if overflow occurred, otherwise false having the...
Definition int_math.hpp:291
constexpr bool sub_overflow(const T a, const T b, T &res) noexcept
Integer overflow aware subtraction returning true if overflow occurred, otherwise false having the re...
Definition int_math.hpp:257
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition int_types.hpp:67
constexpr uint32_t round_to_power_of_2(const uint32_t n)
If the given n is not is_power_of_2() return next_power_of_2(), otherwise return n unchanged.
Definition int_math.hpp:150
constexpr T gcd(T a, T b) noexcept
Returns the greatest common divisor (GCD) of the two given integer values following Euclid's algorith...
Definition int_math.hpp:330
constexpr int sign(const T x) noexcept
Returns the value of the sign function (w/o branching ?) in O(1).
Definition base_math.hpp:84
consteval_cxx20 bool has_builtin_add_overflow() noexcept
Query whether __builtin_add_overflow is available via __has_builtin(__builtin_add_overflow).
Definition int_math.hpp:177
constexpr T round_down(T n, U align_to)
Round down w/ branching in O(1)
Definition int_math.hpp:121
constexpr bool add_overflow(const T a, const T b, T &res) noexcept
Integer overflow aware addition returning true if overflow occurred, otherwise false having the resul...
Definition int_math.hpp:223
consteval_cxx20 bool has_builtin_sub_overflow() noexcept
Query whether __builtin_sub_overflow is available via __has_builtin(__builtin_sub_overflow).
Definition int_math.hpp:188
constexpr bool lcm_overflow(const T a, const T b, T &result) noexcept
Integer overflow aware calculation of least common multiple (LCM) following Euclid's algorithm from E...
Definition int_math.hpp:366
constexpr T abs(const T x) noexcept
Returns the absolute value of an arithmetic number (w/ branching) in O(1)
constexpr bool is_power_of_2(const T x) noexcept
Power of 2 test (w/o branching ?) in O(1)
Definition int_math.hpp:138
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32