aboutsummaryrefslogtreecommitdiff
path: root/intmath.c
diff options
context:
space:
mode:
authorJoel Kronqvist <joelkronqvist@proton.me>2024-03-23 17:56:49 +0200
committerJoel Kronqvist <joelkronqvist@proton.me>2024-03-23 18:00:51 +0200
commite26279107edf7012e6b9cd558aaa09a8f0ca4764 (patch)
tree2bcd2eb3c6c2ade2e85c9f52ca1fc41d3c720811 /intmath.c
downloadstdu-e26279107edf7012e6b9cd558aaa09a8f0ca4764.tar.gz
stdu-e26279107edf7012e6b9cd558aaa09a8f0ca4764.zip
Initial commit
Diffstat (limited to 'intmath.c')
-rw-r--r--intmath.c447
1 files changed, 447 insertions, 0 deletions
diff --git a/intmath.c b/intmath.c
new file mode 100644
index 0000000..cba66a9
--- /dev/null
+++ b/intmath.c
@@ -0,0 +1,447 @@
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "minitest.h"
+#include "intmath.h"
+
+int int_pown(unsigned int base, unsigned int exp) {
+ int res = 1;
+ while (exp > 0) {
+ res *= base;
+ exp--;
+ }
+ return res;
+}
+
+int int_pow10(unsigned int exp) {
+ return int_pown(10, exp);
+}
+
+int int_floor(int x, int precision) {
+ int sign = (x < 0) * -2 + 1;
+ x *= sign;
+
+ int iterations = 0;
+ while (x > int_pow10(precision)) {
+ x /= 10;
+ iterations++;
+ }
+
+ return (x + (sign == -1))
+ * int_pow10(iterations)
+ * sign;
+}
+
+int int_ceil(int x, int precision) {
+ return -int_floor(-x, precision);
+}
+
+int int_min(int a, int b) {
+ return (a < b) * a + (b <= a) * b;
+}
+
+int int_max(int a, int b) {
+ return (a > b) * a + (b >= a) * b;
+}
+
+/* Returns `int x` floored to `unsigned int precision` as an `exp_notated`
+ * value with given `unsigned int base`.
+ *
+ * `unsigned int base` affects the precision.
+ */
+exp_notated int_floored_exponent_notation_base
+(
+ int x,
+ unsigned int precision,
+ unsigned int base
+) {
+ int original = x;
+ int sign = (x < 0) * -2 + 1;
+ x *= sign;
+
+ int i = 0;
+ while (x >= int_pown(base, precision)) {
+ x /= base;
+ i++;
+ }
+ x += (sign == -1) && (original != -x * int_pown(base, i));
+ if (x >= int_pown(base, precision)) {
+ x /= base;
+ i++;
+ }
+ x *= sign;
+
+ exp_notated res = {
+ .mantissa = x,
+ .exponent = i,
+ .base = base
+ };
+ return res;
+}
+
+exp_notated int_floored_exponent_notation(int x, unsigned int precision) {
+ return int_floored_exponent_notation_base(x, precision, 10);
+}
+
+exp_notated int_ceiled_exponent_notation_base(
+ int x,
+ unsigned int precision,
+ unsigned int base
+) {
+ exp_notated res = int_floored_exponent_notation_base(-x, precision, base);
+ res.mantissa = -res.mantissa;
+ return res;
+}
+
+exp_notated int_ceiled_exponent_notation(int x, unsigned int precision) {
+ return int_ceiled_exponent_notation_base(x, precision, 10);
+}
+
+int exp_notated_to_int(exp_notated x) {
+ return x.mantissa * int_pown(x.base, x.exponent);
+}
+
+/* Returns the appropriate binary prefix for `exp_notated x`. `x` must have
+ * `1024` as its base, it should not be greater than `1023*1024^10` and it
+ * should be floored/ceiled (ie. the mantissa should not be further away from
+ * zero than `1023`). Failing to meet these conditions results in the function
+ * returning `NULL`.
+ */
+const char* binary_prefix(exp_notated x) {
+ if (
+ (x.base != 1024) ||
+ (x.exponent > 10) ||
+ (x.mantissa > 1023) ||
+ (x.mantissa < -1023)
+ ) {
+ return NULL;
+ }
+
+ switch (x.exponent) {
+ case 0:
+ return "";
+ case 1:
+ return "Ki";
+ case 2:
+ return "Mi";
+ case 3:
+ return "Gi";
+ case 4:
+ return "Ti";
+ case 5:
+ return "Pi";
+ case 6:
+ return "Ei";
+ case 7:
+ return "Zi";
+ case 8:
+ return "Yi";
+ case 9:
+ return "Ri";
+ default:
+ return "Qi";
+ }
+}
+
+/* Returns the a string describing the argument `int x` floored to the
+ * appropriate multiple of 1024 followed with a binary prefix (Ki, Mi, Gi...).
+ * The string is dynamically allocated and should be free'd.
+ *
+ * Returns NULL on malloc error.
+ */
+char* int_floored_with_binary_prefix(int x) {
+ exp_notated x_exp = int_floored_exponent_notation_base(x, 1, 1024);
+ if (x_exp.exponent > 10) /* quebi-=1024^10 greatest defined prefix */
+ x_exp.mantissa *= int_pow10(x_exp.exponent - 10);
+
+ const char* prefix = binary_prefix(x_exp);
+
+ size_t bufsize =
+ 5 + /* len("-1024") */
+ int_max(0, x_exp.exponent - 10) + /* length of possible zeros */
+ 4; /* len(" Gi\0") */
+ char* res = malloc(bufsize);
+ if (res == NULL) return NULL;
+ sprintf(res, "%d", x_exp.mantissa);
+ if (strcmp(prefix, "") != 0)
+ strcat(res, " ");
+ strcat(res, prefix);
+
+ return res;
+}
+
+char* test_pown() {
+ mt_assert_eq(int_pown(2, 5), 32);
+ mt_assert_eq(int_pown(3, 3), 27);
+ mt_assert_eq(int_pown(9, 0), 1);
+ return 0;
+}
+
+char* test_pow10() {
+ mt_assert_eq(int_pow10(3), 1000);
+ mt_assert_eq(int_pow10(0), 1);
+ return 0;
+}
+
+char* test_floor() {
+ mt_assert_eq(int_floor(34128, 2), 34000);
+ mt_assert_eq(int_floor(-34128, 3), -34200);
+ mt_assert_eq(int_floor(-9999, 3), -10000);
+ mt_assert_eq(int_floor(9999, 3), 9990);
+ mt_assert_eq(int_floor(0, 1), 0);
+ return 0;
+}
+
+char* test_ceil() {
+ mt_assert_eq(int_ceil(44212, 3), 44300);
+ mt_assert_eq(int_ceil(-44212, 3), -44200);
+ mt_assert_eq(int_ceil(-9999, 3), -9990);
+ mt_assert_eq(int_ceil(9999, 3), 10000);
+ mt_assert_eq(int_ceil(0, 1), 0);
+ return 0;
+}
+
+char* test_min() {
+ mt_assert_eq(int_min(2, 6), 2);
+ mt_assert_eq(int_min(4, 1), 1);
+ mt_assert_eq(int_min(10, 10), 1);
+ return 0;
+}
+
+char* test_max() {
+ mt_assert_eq(int_max(3, 7), 7);
+ mt_assert_eq(int_min(4, 1), 4);
+ mt_assert_eq(int_min(10, 10), 10);
+ return 0;
+}
+
+char* test_floored_exponent_notation() {
+ const size_t bufsize = 128;
+ char* msg = malloc(bufsize);
+
+ exp_notated res = int_floored_exponent_notation(9489345, 3);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation(9489345, 3): expected 948*10^4, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == 948 &&
+ res.base == 10 &&
+ res.exponent == 4
+ , msg);
+
+ res = int_floored_exponent_notation(-88, 1);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation(-88, 1): expected -9*10^1, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -9 &&
+ res.base == 10 &&
+ res.exponent == 1
+ , msg);
+
+ res = int_floored_exponent_notation(-99, 1);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation(-99, 1): expected -1*10^2, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1 &&
+ res.base == 10 &&
+ res.exponent == 2
+ , msg);
+
+ res = int_floored_exponent_notation_base(3145733, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation_base(3145733, 1, 1024): expected 3*1024^2, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == 3 &&
+ res.base == 1024 &&
+ res.exponent == 2
+ , msg);
+
+ res = int_floored_exponent_notation_base(-1022, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation(-1022, 1, 1024): expected -1022*1024^0, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1022 &&
+ res.base == 1024 &&
+ res.exponent == 0
+ , msg);
+
+ res = int_floored_exponent_notation_base(-1023, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation(-1023, 1, 1024): expected -1023*1024^0, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1023 &&
+ res.base == 1024 &&
+ res.exponent == 0
+ , msg);
+
+ res = int_floored_exponent_notation_base(-1024, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_floored_exponent_notation(-1024, 1, 1024): expected -1*1024^1, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1 &&
+ res.base == 1024 &&
+ res.exponent == 1
+ , msg);
+
+ free(msg);
+ return 0;
+}
+
+char* test_ceiled_exponent_notation() {
+ const size_t bufsize = 128;
+ char* msg = malloc(bufsize);
+
+ exp_notated res = int_ceiled_exponent_notation(9489345, 3);
+ snprintf(msg, bufsize,
+ "int_ceiled_exponent_notation(9489345, 3): expected 949*10^4, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == 949 &&
+ res.base == 10 &&
+ res.exponent == 4
+ , msg);
+
+ res = int_ceiled_exponent_notation(-99, 1);
+ snprintf(msg, bufsize,
+ "int_ceiled_exponent_notation(-99, 1): expected -9*10^1, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -9 &&
+ res.base == 10 &&
+ res.exponent == 1
+ , msg);
+
+ res = int_ceiled_exponent_notation(-100, 1);
+ snprintf(msg, bufsize,
+ "int_ceiled_exponent_notation(-100, 1): expected -1*10^2, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1 &&
+ res.base == 10 &&
+ res.exponent == 2
+ , msg);
+
+ res = int_ceiled_exponent_notation_base(3145733, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_ceiled_exponent_notation_base(3145733, 1, 1024): expected 4*1024^2, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == 4 &&
+ res.base == 1024 &&
+ res.exponent == 2
+ , msg);
+
+ res = int_ceiled_exponent_notation_base(-1023, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_ceiled_exponent_notation(-1023, 1, 1024): expected -1023*1024^0, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1023 &&
+ res.base == 1024 &&
+ res.exponent == 0
+ , msg);
+
+ res = int_ceiled_exponent_notation_base(-1024, 1, 1024);
+ snprintf(msg, bufsize,
+ "int_ceiled_exponent_notation(-1024, 1, 1024): expected -1*1024^1, got %d*%d^%d",
+ res.mantissa, res.base, res.exponent
+ );
+ mt_assert(
+ res.mantissa == -1 &&
+ res.base == 1024 &&
+ res.exponent == 1
+ , msg);
+
+ free(msg);
+ return 0;
+}
+
+char* test_exponent_notated_to_int() {
+ exp_notated x = {
+ .mantissa = 3,
+ .exponent = 2,
+ .base = 10
+ };
+ mt_assert_eq(300, exp_notated_to_int(x));
+ return 0;
+}
+
+char* test_floored_with_binary_prefix() {
+ const size_t bufsize = 64;
+ char* msg = malloc(bufsize);
+
+ char* res = int_floored_with_binary_prefix(1026);
+ snprintf(msg, bufsize, "1026 yielded `%s` instead of `1 Ki`", res);
+ mt_assert(strcmp(
+ res,
+ "1 Ki"
+ ) == 0, msg);
+ free(res);
+
+ res = int_floored_with_binary_prefix(-1023);
+ snprintf(msg, bufsize, "-1023 yielded `%s` instead of `-1023`", res);
+ mt_assert(strcmp(
+ res,
+ "-1023"
+ ) == 0, msg);
+ free(res);
+
+ res = int_floored_with_binary_prefix(-1026);
+ snprintf(msg, bufsize, "-1026 yielded `%s` instead of `-2 Ki`", res);
+ mt_assert(strcmp(
+ res,
+ "-2 Ki"
+ ) == 0, msg);
+ free(res);
+
+ res = int_floored_with_binary_prefix(1049088);
+ snprintf(msg, bufsize, "1049088 yielded `%s` instead of `1 Mi`", res);
+ mt_assert(strcmp(
+ res,
+ "1 Mi"
+ ) == 0, msg);
+ free(res);
+
+ res = int_floored_with_binary_prefix(5243392);
+ snprintf(msg, bufsize, "5243392 yielded `%s` instead of `5 Mi`", res);
+ mt_assert(strcmp(
+ res,
+ "5 Mi"
+ ) == 0, msg);
+ free(res);
+
+ free(msg);
+ return 0;
+}
+
+void intmath_tests() {
+ mt_run_test(test_pown);
+ mt_run_test(test_pow10);
+ mt_run_test(test_floor);
+ mt_run_test(test_ceil);
+ mt_run_test(test_min);
+ mt_run_test(test_max);
+ mt_run_test(test_floored_exponent_notation);
+ mt_run_test(test_ceiled_exponent_notation);
+ mt_run_test(test_exponent_notated_to_int);
+ mt_run_test(test_floored_with_binary_prefix);
+}