diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d5c0bb9d..bfc718ee 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(levenshtein) add_subdirectory(list) add_subdirectory(log) add_subdirectory(log2) +add_subdirectory(math) add_subdirectory(matrix) add_subdirectory(mpi) add_subdirectory(notifier) diff --git a/examples/math/.gitignore b/examples/math/.gitignore new file mode 100644 index 00000000..cadac6b5 --- /dev/null +++ b/examples/math/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +/math-abs diff --git a/examples/math/CMakeLists.txt b/examples/math/CMakeLists.txt new file mode 100644 index 00000000..14e6e329 --- /dev/null +++ b/examples/math/CMakeLists.txt @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright(c) 2023 ffashion +# + +add_executable(math-abs abs.c) +target_link_libraries(math-abs bfdev) +add_test(math-abs math-abs) + +if(${CMAKE_PROJECT_NAME} STREQUAL "bfdev") + install(FILES + abs.c + DESTINATION + ${CMAKE_INSTALL_DOCDIR}/examples/list + ) + + install(TARGETS + math-abs + DESTINATION + ${CMAKE_INSTALL_DOCDIR}/bin + ) +endif() diff --git a/examples/math/abs.c b/examples/math/abs.c new file mode 100644 index 00000000..e334fbc0 --- /dev/null +++ b/examples/math/abs.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * Copyright(c) 2024 John Sanpe + */ + +#define MODULE_NAME "math-abs" +#define bfdev_log_fmt(fmt) MODULE_NAME ": " fmt + +#include +#include +#include + +#define ABS_TEST(name, min, max) ({ \ + long long __result; \ + __result = (long long)bfdev_abs(min); \ + bfdev_log_info(name " %lld -> %lld\n", \ + (long long)min, __result); \ + if (max != __result) { \ + bfdev_log_err("test failed"); \ + return 1; \ + } \ +}) + +int +main(int argc, const char *argv[]) +{ + ABS_TEST("char", BFDEV_CHAR_MIN + 1, BFDEV_CHAR_MAX); + ABS_TEST("short", BFDEV_SHRT_MIN + 1, BFDEV_SHRT_MAX); + ABS_TEST("int", BFDEV_INT_MIN + 1, BFDEV_INT_MAX); + ABS_TEST("long", BFDEV_LONG_MIN + 1, BFDEV_LONG_MAX); + ABS_TEST("long long", BFDEV_LLONG_MIN + 1, BFDEV_LLONG_MAX); + + return 0; +} diff --git a/include/bfdev/math.h b/include/bfdev/math.h index 36f17250..8de107cf 100644 --- a/include/bfdev/math.h +++ b/include/bfdev/math.h @@ -38,6 +38,79 @@ BFDEV_BEGIN_DECLS (x) & ~bfdev_round_mask(x, y) \ ) +#define bfdev_abs_expr(var, type, other) __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(var), signed type) || \ + __builtin_types_compatible_p(typeof(var), unsigned type), \ + ({ \ + signed type __var; \ + __var = (signed type)(var); \ + __var < 0 ? - __var : __var; \ + }), other \ +) + +/** + * bfdev_abs - return absolute value of an argument. + * + * If it is unsigned type, it is converted to signed type first. + * char is treated as if it was signed but the macro's + * return type is preserved as char. + */ +#define bfdev_abs(var) \ + bfdev_abs_expr(var, long long, \ + bfdev_abs_expr(var, long, \ + bfdev_abs_expr(var, int, \ + bfdev_abs_expr(var, short, \ + bfdev_abs_expr(var, char, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p( \ + typeof(var), char), \ + (char) ({ \ + signed char __var; \ + __var = (signed char)(var); \ + __var < 0 ? - __var : __var; \ + }), \ + ((void)0) \ + ) \ + )))) \ +) + +/** + * bfdev_abs_diff - return absolute value of the difference between the args. + * @var1: the first argument. + * @var2: the second argument. + */ +#define bfdev_abs_diff(var1, var2) ({ \ + typeof(var1) __var1, __var2; \ + \ + __var1 = (typeof(var1))(var1); \ + __var2 = (typeof(var1))(var2); \ + \ + __var1 > __var2 \ + ? (__var1 - __var2) \ + : (__var2 - __var1); \ +}) + +/** + * BFDEV_MULT_FRAC + * + * Calculate "value * numer / denom" + * without unnecessary overflow or loss of precision. + */ +#define BFDEV_MULT_FRAC(value, numer, denom) ({ \ + typeof(value) __value, __numer, __denom; \ + typeof(value) __quot, __rem; \ + \ + __value = (typeof(value))(value); \ + __numer = (typeof(value))(numer); \ + __denom = (typeof(value))(denom); \ + \ + __quot = (__value) / (__denom); \ + __rem = (__value) % (__denom); \ + \ + (__quot * (__numer)) + \ + ((__rem * (__numer)) / (__denom)); \ +}) + /** * BFDEV_DIV_ROUND_UP - round up division. * @n: divisor number.