From e86841ea6e3a6d9afc08fca71c26ffda0a627a8f Mon Sep 17 00:00:00 2001 From: hellerve Date: Thu, 3 Aug 2017 16:22:17 -0400 Subject: [PATCH] div: added long division, fixed a few bugs --- README.md | 3 ++- silly.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- silly.h | 1 + tests/test.c | 43 +++++++++++++++++++++++++++++++++---------- 4 files changed, 82 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5579489..7e4d594 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ double silly_to_double(silly); // convert a silly number into a double silly silly_add(silly, silly); // addition silly silly_sub(silly, silly); // subtraction silly silly_mul(silly, silly); // multiplication -silly silly_div(silly, silly); // division (integer precision only :( ) +silly silly_idiv(silly, silly); // division (integer precision) +silly silly_div(silly, silly); // division char* silly_to_string(silly); // converts a fixed point value to a string (memory is now yours) ``` diff --git a/silly.c b/silly.c index 28d4234..6d4dc11 100644 --- a/silly.c +++ b/silly.c @@ -77,7 +77,7 @@ silly silly_mul(silly x, silly y) { return z; } -silly silly_div(silly x, silly y) { +silly silly_idiv(silly x, silly y) { silly z; z.sign = x.sign ^ y.sign; @@ -89,10 +89,53 @@ silly silly_div(silly x, silly y) { return z; } +int count_zeroes(uint64_t x) { + int res = 0; + while (!(x & 0xf000000000000000)) { res += 4; x <<= 4; } + while (!(x & 0x8000000000000000)) { res += 1; x <<= 1; } + return res; +} + +silly silly_div(silly x, silly y) { + uint64_t x0 = (((uint64_t) x.before) << 32) + x.after; + uint64_t y0 = (((uint64_t) y.before) << 32) + y.after; + + uint64_t rem = x0; + uint64_t div = y0; + uint64_t quo = 0UL; + int b = 33; // 64 / 2 + 1 + + while ((div & 0xF) == 0 && b >= 4) { + div >>= 4; + b -= 4; + } + + while (rem && b >= 0) { + int s = count_zeroes(rem); + if (s > b) s = b; + + rem <<= s; + b -= s; + + uint64_t d = rem / div; + rem %= div; + quo += d << b; + + rem <<= 1; + --b; + } + + // rounding + //++quo; + uint64_t res = quo >> 1; + + return make_silly(x.sign ^ y.sign, res >> 32, res & 0xffffffff); +} + char* silly_to_string(silly s) { char* res = malloc(23); - snprintf(res, 23, "%s%010d.%010d", s.sign? "-" : "+", s.before, s.after); + snprintf(res, 23, "%s%010d.%010llu", s.sign? "-" : "+", s.before, (uint64_t)((s.after/((double)0xffffffff))*(uint64_t)10000000000)); return res; } @@ -111,11 +154,10 @@ silly make_silly(short sign, int before, int after) { #define FROM(n) {\ silly s;\ - double _;\ s.sign = signbit(x);\ x = fabs(x);\ s.before = trunc(x);\ - s.after = modf(x, &_);\ + s.after = (uint32_t)((x-(long)x)*0xffffffff);\ return s;\ } diff --git a/silly.h b/silly.h index 1303998..a44aca3 100644 --- a/silly.h +++ b/silly.h @@ -15,6 +15,7 @@ silly make_silly(short, int, int); silly silly_add(silly, silly); silly silly_sub(silly, silly); silly silly_mul(silly, silly); +silly silly_idiv(silly, silly); silly silly_div(silly, silly); silly silly_from_float(float); diff --git a/tests/test.c b/tests/test.c index 1651cb9..8f83fa1 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1,6 +1,13 @@ #include "greatest.h" #include "../silly.h" +#define DELTA 0.01 + +#define ASSERT_DELTA(expected, actual) {\ + double delta = expected-actual;\ + ASSERT_IN_RANGE(0, delta, DELTA);\ +} + TEST silly_zeros_is_zero() { silly x = silly_zeros(); @@ -99,19 +106,35 @@ TEST silly_conversion() { x.sign = 1; x.before = 1; x.after = (int)(((double)0xffffffff)/10); - ASSERT(-1.1 - silly_to_double(x) <= 0.01); + ASSERT_DELTA(-1.1, silly_to_double(x)); x = silly_from_float(0.0); ASSERT_EQ_FMT(0.0, silly_to_double(x), "%f"); x = silly_from_float(-1.1); - ASSERT(-1.1 - silly_to_double(x) <= 0.01); + ASSERT_DELTA(-1.1, silly_to_double(x)); x = silly_from_double((double) 0.0); ASSERT_EQ_FMT(0.0, silly_to_double(x), "%f"); x = silly_from_double((double) -1.1); - ASSERT(-1.1 - silly_to_double(x) <= 0.01); + ASSERT_DELTA(-1.1, silly_to_double(x)); x = silly_from_double((double) -413.25); - ASSERT(-413.25 - silly_to_double(x) <= 0.01); + ASSERT_DELTA(-413.25, silly_to_double(x)); + + PASS(); +} + +TEST silly_integral_division() { + silly x = make_silly(0, 10, 0); + silly y = make_silly(1, 5, 0); + + ASSERT_EQ_FMT(-2.0, silly_to_double(silly_idiv(x, y)), "%f"); + + y = make_silly(1, 2, 5); + + ASSERT_EQ_FMT(-4.0, silly_to_double(silly_idiv(x, y)), "%f"); + + y = make_silly(1, 2, 3); + ASSERT_EQ_FMT(-4.0, silly_to_double(silly_idiv(x, y)), "%f"); PASS(); } @@ -122,14 +145,13 @@ TEST silly_division() { ASSERT_EQ_FMT(-2.0, silly_to_double(silly_div(x, y)), "%f"); - y = make_silly(1, 2, 5); + y = make_silly(1, 2, 0xffffffff/2); - ASSERT_EQ_FMT(-4.0, silly_to_double(silly_div(x, y)), "%f"); + ASSERT_DELTA(-4.0, silly_to_double(silly_div(x, y))); - // :( - y = make_silly(1, 2, 3); - - ASSERT_EQ_FMT(-4.0, silly_to_double(silly_div(x, y)), "%f"); + x = make_silly(0, 4, 0); + y = make_silly(0, 8, 0); + ASSERT_DELTA(0.5, silly_to_double(silly_div(x, y))); PASS(); } @@ -140,6 +162,7 @@ SUITE(tests) { RUN_TEST(silly_addition); RUN_TEST(silly_subtraction); RUN_TEST(silly_multiplication); + RUN_TEST(silly_integral_division); RUN_TEST(silly_division); RUN_TEST(silly_conversion); }