diff --git a/c-programming/math/opt_int_div.c b/c-programming/math/opt_int_div.c new file mode 100644 index 0000000..78d2a5d --- /dev/null +++ b/c-programming/math/opt_int_div.c @@ -0,0 +1,44 @@ +/* + * opt_int_div.c + * + * Author: Intel A80486DX2-66 + * License: Creative Commons Zero 1.0 Universal + */ + +#include "opt_int_div.h" + +#ifdef TEST +# include +# include + +int main(void) { + printf("Loop: a > 0, b > 0\n"); + for (uint8_t a = 3; a <= 24; a++) + for (uint8_t b = 1; b <= 8; b++) { + if (b >= a) + break; + printf("%" PRIu8 " / %" PRIu8 " = %" PRIu8 "\n", a, b, + OPT_INT_DIV(a, b)); + } + + printf("Loop: a < 0, b > 0\n"); + for (int8_t a = -3; a >= -24; a--) + for (int8_t b = 1; b <= 8; b++) { + if (b >= abs(a)) + break; + printf("%" PRId8 " / %" PRId8 " = %" PRId8 "\n", a, b, + OPT_INT_DIV(a, b)); + } + + printf("Loop: a < 0, b < 0\n"); + for (int8_t a = -3; a >= -24; a--) + for (int8_t b = -1; b >= -8; b--) { + if (b <= a) + break; + printf("%" PRId8 " / %" PRId8 " = %" PRId8 "\n", a, b, + OPT_INT_DIV(a, b)); + } + + return 0; +} +#endif diff --git a/c-programming/math/opt_int_div.h b/c-programming/math/opt_int_div.h new file mode 100644 index 0000000..10a83fc --- /dev/null +++ b/c-programming/math/opt_int_div.h @@ -0,0 +1,47 @@ +/* + * opt_int_div.h + * + * Author: Intel A80486DX2-66 + * License: Creative Commons Zero 1.0 Universal + */ + +#include +#include + +// parameters +#define OPT_INT_DIV_TEST_PRECISION 8 + +// constants +#define OPT_INT_DIV_ROUNDING \ + /* 10^OPT_INT_DIV_TEST_PRECISION */ \ + powl(10.l, (long double) OPT_INT_DIV_TEST_PRECISION) \ + +// helper functions +#define INT_BIN_DIV(a, b) ((a) >> (uintmax_t) log2l((b))) +#define INT_DIV_NEG_RESULT_SIGN(a, b) (((a) < 0) ^ ((b) < 0)) +#define INT_ABS(x) ((x) < 0 ? -(x) : (x)) + +// the main macro +#define OPT_INT_DIV(a, b) \ + ( /* beginning */ \ + /* check for equality of a and b */ \ + INT_ABS((a)) == INT_ABS((b)) ? \ + /* if only one of the numbers is negative, the result is -1, */ \ + /* otherwise it is 1. */ \ + (INT_DIV_NEG_RESULT_SIGN(a, b) ? -1 : 1) : ( \ + \ + /* check if b is a power of 2 */ \ + /* */ \ + /* formula: round(log_2(b) % 1, OPT_INT_DIV_TEST_PRECISION digits */ \ + /* after point) == 0 */ \ + (roundl(fmodl(log2l((b)), 1.l) * OPT_INT_DIV_ROUNDING) / \ + OPT_INT_DIV_ROUNDING == 0.l) ? ( \ + /* if only one of the numbers is negative, the result is */ \ + /* negative */ \ + INT_DIV_NEG_RESULT_SIGN(a, b) ? \ + -INT_BIN_DIV(INT_ABS(a), INT_ABS(b)) \ + : \ + INT_BIN_DIV(INT_ABS(a), INT_ABS(b))) \ + : \ + ((a) / (b)) ) \ + ) /* end */