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