div: added long division, fixed a few bugs
This commit is contained in:
@@ -22,7 +22,8 @@ double silly_to_double(silly); // convert a silly number into a double
|
|||||||
silly silly_add(silly, silly); // addition
|
silly silly_add(silly, silly); // addition
|
||||||
silly silly_sub(silly, silly); // subtraction
|
silly silly_sub(silly, silly); // subtraction
|
||||||
silly silly_mul(silly, silly); // multiplication
|
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)
|
char* silly_to_string(silly); // converts a fixed point value to a string (memory is now yours)
|
||||||
```
|
```
|
||||||
|
50
silly.c
50
silly.c
@@ -77,7 +77,7 @@ silly silly_mul(silly x, silly y) {
|
|||||||
return z;
|
return z;
|
||||||
}
|
}
|
||||||
|
|
||||||
silly silly_div(silly x, silly y) {
|
silly silly_idiv(silly x, silly y) {
|
||||||
silly z;
|
silly z;
|
||||||
z.sign = x.sign ^ y.sign;
|
z.sign = x.sign ^ y.sign;
|
||||||
|
|
||||||
@@ -89,10 +89,53 @@ silly silly_div(silly x, silly y) {
|
|||||||
return z;
|
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* silly_to_string(silly s) {
|
||||||
char* res = malloc(23);
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -111,11 +154,10 @@ silly make_silly(short sign, int before, int after) {
|
|||||||
|
|
||||||
#define FROM(n) {\
|
#define FROM(n) {\
|
||||||
silly s;\
|
silly s;\
|
||||||
double _;\
|
|
||||||
s.sign = signbit(x);\
|
s.sign = signbit(x);\
|
||||||
x = fabs(x);\
|
x = fabs(x);\
|
||||||
s.before = trunc(x);\
|
s.before = trunc(x);\
|
||||||
s.after = modf(x, &_);\
|
s.after = (uint32_t)((x-(long)x)*0xffffffff);\
|
||||||
return s;\
|
return s;\
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
silly.h
1
silly.h
@@ -15,6 +15,7 @@ silly make_silly(short, int, int);
|
|||||||
silly silly_add(silly, silly);
|
silly silly_add(silly, silly);
|
||||||
silly silly_sub(silly, silly);
|
silly silly_sub(silly, silly);
|
||||||
silly silly_mul(silly, silly);
|
silly silly_mul(silly, silly);
|
||||||
|
silly silly_idiv(silly, silly);
|
||||||
silly silly_div(silly, silly);
|
silly silly_div(silly, silly);
|
||||||
|
|
||||||
silly silly_from_float(float);
|
silly silly_from_float(float);
|
||||||
|
43
tests/test.c
43
tests/test.c
@@ -1,6 +1,13 @@
|
|||||||
#include "greatest.h"
|
#include "greatest.h"
|
||||||
#include "../silly.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() {
|
TEST silly_zeros_is_zero() {
|
||||||
silly x = silly_zeros();
|
silly x = silly_zeros();
|
||||||
|
|
||||||
@@ -99,19 +106,35 @@ TEST silly_conversion() {
|
|||||||
x.sign = 1;
|
x.sign = 1;
|
||||||
x.before = 1;
|
x.before = 1;
|
||||||
x.after = (int)(((double)0xffffffff)/10);
|
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);
|
x = silly_from_float(0.0);
|
||||||
ASSERT_EQ_FMT(0.0, silly_to_double(x), "%f");
|
ASSERT_EQ_FMT(0.0, silly_to_double(x), "%f");
|
||||||
x = silly_from_float(-1.1);
|
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);
|
x = silly_from_double((double) 0.0);
|
||||||
ASSERT_EQ_FMT(0.0, silly_to_double(x), "%f");
|
ASSERT_EQ_FMT(0.0, silly_to_double(x), "%f");
|
||||||
x = silly_from_double((double) -1.1);
|
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);
|
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();
|
PASS();
|
||||||
}
|
}
|
||||||
@@ -122,14 +145,13 @@ TEST silly_division() {
|
|||||||
|
|
||||||
ASSERT_EQ_FMT(-2.0, silly_to_double(silly_div(x, y)), "%f");
|
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)));
|
||||||
|
|
||||||
// :(
|
x = make_silly(0, 4, 0);
|
||||||
y = make_silly(1, 2, 3);
|
y = make_silly(0, 8, 0);
|
||||||
|
ASSERT_DELTA(0.5, silly_to_double(silly_div(x, y)));
|
||||||
ASSERT_EQ_FMT(-4.0, silly_to_double(silly_div(x, y)), "%f");
|
|
||||||
|
|
||||||
PASS();
|
PASS();
|
||||||
}
|
}
|
||||||
@@ -140,6 +162,7 @@ SUITE(tests) {
|
|||||||
RUN_TEST(silly_addition);
|
RUN_TEST(silly_addition);
|
||||||
RUN_TEST(silly_subtraction);
|
RUN_TEST(silly_subtraction);
|
||||||
RUN_TEST(silly_multiplication);
|
RUN_TEST(silly_multiplication);
|
||||||
|
RUN_TEST(silly_integral_division);
|
||||||
RUN_TEST(silly_division);
|
RUN_TEST(silly_division);
|
||||||
RUN_TEST(silly_conversion);
|
RUN_TEST(silly_conversion);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user