From 0c88e55fa8f132e8ac15426911d155f136078302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ba=C5=99inka?= Date: Mon, 3 May 2021 12:43:54 +0200 Subject: [PATCH] fixed division, undefined behaviors and some improvements (#33) * testing using ctest * emove old testing script * added github workflow CI * updated CI * added unit test for macros * F16() and F16C() are both rounding allways, so fix16_from_dbl should as well * added tests for strign operations, but these functions are in my opinion unreliable and tests are failing * removed old unittests * removed old unittests from cmake * problem with division using gcc * improved benchmark * clarification of problem with division * attempt to fix * fixed some undefined behaviors, fixed division --- CMakeLists.txt | 5 - benchmarks/Makefile | 4 +- benchmarks/benchmark.c | 45 ++-- benchmarks/interface-arm.c | 4 - benchmarks/interface-avr.c | 5 - benchmarks/interface.c | 12 + benchmarks/interface.h | 11 + benchmarks/results-avr.ods | Bin 0 -> 18676 bytes libfixmath/fix16.c | 37 +-- libfixmath/fix16.h | 20 +- libfixmath/fix16_sqrt.c | 135 +++++----- tests/tests.c | 38 ++- tests/tests.cmake | 7 + tests/tests.h | 33 ++- tests/tests_basic.c | 10 +- tests/tests_macros.c | 100 ++++++++ tests/tests_macros.h | 6 + tests/tests_str.c | 108 ++++++++ tests/tests_str.h | 6 + unittests/Makefile | 78 ------ unittests/fix16_exp_unittests.c | 124 --------- unittests/fix16_macros_unittests.c | 110 -------- unittests/fix16_str_unittests.c | 117 --------- unittests/fix16_unittests.c | 391 ----------------------------- unittests/run_unittests.sh | 16 -- unittests/unittests.h | 18 -- 26 files changed, 445 insertions(+), 995 deletions(-) create mode 100644 benchmarks/interface.c create mode 100644 benchmarks/results-avr.ods create mode 100644 tests/tests_macros.c create mode 100644 tests/tests_macros.h create mode 100644 tests/tests_str.c create mode 100644 tests/tests_str.h delete mode 100644 unittests/Makefile delete mode 100644 unittests/fix16_exp_unittests.c delete mode 100644 unittests/fix16_macros_unittests.c delete mode 100644 unittests/fix16_str_unittests.c delete mode 100644 unittests/fix16_unittests.c delete mode 100644 unittests/run_unittests.sh delete mode 100644 unittests/unittests.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 204ef3b..6dae22c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,6 @@ include(tests/tests.cmake) file(GLOB fixsingen-srcs fixsingen/*.c) file(GLOB fixtest-srcs fixtest/*.c fixtest/*.h) -file(GLOB unittests-srcs unittests/*.c unittests/*.h) add_executable(fixtest ${fixtest-srcs}) target_link_libraries(fixtest PRIVATE libfixmath m) @@ -25,8 +24,4 @@ add_executable(fixsingen ${fixsingen-srcs}) target_link_libraries(fixsingen PRIVATE libfixmath m) target_include_directories(fixsingen PRIVATE ${CMAKE_SOURCE_DIR}) -add_executable(unittests ${unittests-srcs}) -target_link_libraries(unittests PRIVATE libfixmath m) -target_include_directories(unittests PRIVATE ${CMAKE_SOURCE_DIR}) - diff --git a/benchmarks/Makefile b/benchmarks/Makefile index 6331596..488e1a7 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -2,9 +2,9 @@ # (currently ARM Cortex M3 and AVR). They are a bit tricky to run, as they # depend on specific simulator versions. -FILES = benchmark.c ../libfixmath/fix16.c ../libfixmath/fix16_sqrt.c ../libfixmath/fix16_exp.c +FILES = benchmark.c interface.c ../libfixmath/fix16.c ../libfixmath/fix16_sqrt.c ../libfixmath/fix16_exp.c -CFLAGS = -DFIXMATH_NO_OVERFLOW -DFIXMATH_NO_ROUNDING -ffast-math -I../libfixmath +CFLAGS = -std=gnu11 -I../libfixmath .PHONY clean: rm -f *.elf diff --git a/benchmarks/benchmark.c b/benchmarks/benchmark.c index 70a2aeb..3942abc 100644 --- a/benchmarks/benchmark.c +++ b/benchmarks/benchmark.c @@ -9,15 +9,6 @@ /* Autogenerated testcases */ #include "testcases.c" -/* Tools for profiling */ - -typedef struct { - uint32_t min; - uint32_t max; - uint32_t sum; - uint32_t count; -} cyclecount_t; - // Initializer for cyclecount_t structure. // Max is initialized to 0 and min is 2^32-1 so that first call to cyclecount_update will set them. #define CYCLECOUNT_INIT {0xFFFFFFFF, 0, 0, 0} @@ -40,12 +31,6 @@ static void cyclecount_update(cyclecount_t *data, uint32_t cycles) cyclecount_update(&variable, end_timing()); \ } -#define PRINT(variable, label) { \ - print_value(label " min", variable.min); \ - print_value(label " max", variable.max); \ - print_value(label " avg", variable.sum / variable.count); \ -} - static cyclecount_t exp_cycles = CYCLECOUNT_INIT; static cyclecount_t sqrt_cycles = CYCLECOUNT_INIT; static cyclecount_t add_cycles = CYCLECOUNT_INIT; @@ -53,13 +38,12 @@ static cyclecount_t sub_cycles = CYCLECOUNT_INIT; static cyclecount_t div_cycles = CYCLECOUNT_INIT; static cyclecount_t mul_cycles = CYCLECOUNT_INIT; -#ifndef NO_FLOAT static cyclecount_t float_sqrtf_cycles = CYCLECOUNT_INIT; +static cyclecount_t float_expf_cycles = CYCLECOUNT_INIT; static cyclecount_t float_add_cycles = CYCLECOUNT_INIT; static cyclecount_t float_sub_cycles = CYCLECOUNT_INIT; static cyclecount_t float_div_cycles = CYCLECOUNT_INIT; static cyclecount_t float_mul_cycles = CYCLECOUNT_INIT; -#endif static fix16_t delta(fix16_t result, fix16_t expected) { @@ -119,8 +103,6 @@ int main() print_value("Failed EXP, expected", expected); } } - PRINT(sqrt_cycles, "fix16_sqrt"); - PRINT(exp_cycles, "fix16_exp"); for (i = 0; i < TESTCASES2_COUNT; i++) { @@ -175,10 +157,6 @@ int main() } } } - PRINT(add_cycles, "fix16_add"); - PRINT(sub_cycles, "fix16_sub"); - PRINT(mul_cycles, "fix16_mul"); - PRINT(div_cycles, "fix16_div"); /* Compare with floating point performance */ #ifndef NO_FLOAT @@ -187,8 +165,8 @@ int main() float input = fix16_to_float(testcases1[i].a); volatile float result; MEASURE(float_sqrtf_cycles, result = sqrtf(input)); + MEASURE(float_expf_cycles, result = expf(input)); } - PRINT(float_sqrtf_cycles, "float sqrtf"); for (i = 0; i < TESTCASES2_COUNT; i++) { @@ -204,11 +182,20 @@ int main() MEASURE(float_div_cycles, result = a / b); } } - PRINT(float_add_cycles, "float add"); - PRINT(float_sub_cycles, "float sub"); - PRINT(float_mul_cycles, "float mul"); - PRINT(float_div_cycles, "float div"); -#endif +#endif + + print("fix16_sqrt", &sqrt_cycles); + print("float sqrtf", &float_sqrtf_cycles); + print("fix16_exp", &exp_cycles); + print("float expf", &float_expf_cycles); + print("fix16_add", &add_cycles); + print("float add", &float_add_cycles); + print("fix16_sub", &sub_cycles); + print("float sub", &float_sub_cycles); + print("fix16_mul", &mul_cycles); + print("float mul", &float_mul_cycles); + print("fix16_div", &div_cycles); + print("float div", &float_div_cycles); return 0; } diff --git a/benchmarks/interface-arm.c b/benchmarks/interface-arm.c index cd37979..d0e9969 100644 --- a/benchmarks/interface-arm.c +++ b/benchmarks/interface-arm.c @@ -25,8 +25,4 @@ uint16_t end_timing() return 0x00FFFFFF - STCURRENT - 4; } -void print_value(const char *label, int32_t value) -{ - printf("%-20s %ld\n", label, value); -} diff --git a/benchmarks/interface-avr.c b/benchmarks/interface-avr.c index 02731aa..c3bd4c2 100644 --- a/benchmarks/interface-avr.c +++ b/benchmarks/interface-avr.c @@ -32,8 +32,3 @@ uint16_t end_timing() { return TCNT1 - 9; } - -void print_value(const char *label, int32_t value) -{ - printf("%-20s %ld\n", label, value); -} diff --git a/benchmarks/interface.c b/benchmarks/interface.c new file mode 100644 index 0000000..7f37648 --- /dev/null +++ b/benchmarks/interface.c @@ -0,0 +1,12 @@ +#include +#include "interface.h" + +void print_value(const char *label, int32_t value) +{ + printf("%-20s %6ld\n", label, value); +} + +void print(const char *label, cyclecount_t *count) +{ + printf("%-20s %6ld %6ld %6ld\n",label,count->min, count->sum / count->count, count->max); +} diff --git a/benchmarks/interface.h b/benchmarks/interface.h index f8c5117..cf6a3f9 100644 --- a/benchmarks/interface.h +++ b/benchmarks/interface.h @@ -3,6 +3,15 @@ #include +/* Tools for profiling */ + +typedef struct { + uint32_t min; + uint32_t max; + uint32_t sum; + uint32_t count; +} cyclecount_t; + // Initialize void interface_init(); @@ -14,3 +23,5 @@ uint16_t end_timing(); // Print a value to console, along with a descriptive label void print_value(const char *label, int32_t value); + +void print(const char *label, cyclecount_t *count); diff --git a/benchmarks/results-avr.ods b/benchmarks/results-avr.ods new file mode 100644 index 0000000000000000000000000000000000000000..29a089474eb8cbfcc197e802cb02f8db31658edf GIT binary patch literal 18676 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

N$p4TrDKpEl;6J-14To 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 T>jD6vy#HB&003AB{LKA-&n3b5 literal 0 HcmV?d00001 diff --git a/libfixmath/fix16.c b/libfixmath/fix16.c index 4b1ae2b..5063544 100644 --- a/libfixmath/fix16.c +++ b/libfixmath/fix16.c @@ -10,7 +10,8 @@ fix16_t fix16_add(fix16_t a, fix16_t b) { // Use unsigned integers because overflow with signed integers is // an undefined operation (http://www.airs.com/blog/archives/120). - uint32_t _a = a, _b = b; + uint32_t _a = a; + uint32_t _b = b; uint32_t sum = _a + _b; // Overflow can only happen if sign of a == sign of b, and then @@ -23,7 +24,8 @@ fix16_t fix16_add(fix16_t a, fix16_t b) fix16_t fix16_sub(fix16_t a, fix16_t b) { - uint32_t _a = a, _b = b; + uint32_t _a = a; + uint32_t _b = b; uint32_t diff = _a - _b; // Overflow can only happen if sign of a != sign of b, and then @@ -173,8 +175,8 @@ fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) #if defined(FIXMATH_OPTIMIZE_8BIT) fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) { - uint32_t _a = (inArg0 >= 0) ? inArg0 : (-inArg0); - uint32_t _b = (inArg1 >= 0) ? inArg1 : (-inArg1); + uint32_t _a = fix_abs(inArg0); + uint32_t _b = fix_abs(inArg1); uint8_t va[4] = {_a, (_a >> 8), (_a >> 16), (_a >> 24)}; uint8_t vb[4] = {_b, (_b >> 8), (_b >> 16), (_b >> 24)}; @@ -218,7 +220,7 @@ fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) // i = 2 if (va[0] && vb[2]) mid += (uint16_t)va[0] * vb[2]; if (va[1] && vb[1]) mid += (uint16_t)va[1] * vb[1]; - if (va[2] && vb[0]) mid += (uint16_t)va[2] * vb[0]; + if (va[2] && vb[0]) mid += (uint16_t)va[2] * vb[0]; // i = 1 if (va[0] && vb[1]) low += (uint16_t)va[0] * vb[1]; @@ -295,20 +297,21 @@ fix16_t fix16_div(fix16_t a, fix16_t b) if (b == 0) return fix16_minimum; - uint32_t remainder = (a >= 0) ? a : (-a); - uint32_t divider = (b >= 0) ? b : (-b); - uint32_t quotient = 0; - int bit_pos = 17; - + uint32_t remainder = fix_abs(a); + uint32_t divider = fix_abs(b); + uint64_t quotient = 0; + int bit_pos = 17; + // Kick-start the division a bit. // This improves speed in the worst-case scenarios where N and D are large // It gets a lower estimate for the result by N/(D >> 17 + 1). if (divider & 0xFFF00000) { uint32_t shifted_div = ((divider >> 17) + 1); - quotient = remainder / shifted_div; - remainder -= ((uint64_t)quotient * divider) >> 17; - } + quotient = remainder / shifted_div; + uint64_t tmp = ((uint64_t)quotient * (uint64_t)divider) >> 17; + remainder -= (uint32_t)(tmp); + } // If the divider is divisible by 2^n, take advantage of it. while (!(divider & 0xF) && bit_pos >= 4) @@ -326,8 +329,8 @@ fix16_t fix16_div(fix16_t a, fix16_t b) bit_pos -= shift; uint32_t div = remainder / divider; - remainder = remainder % divider; - quotient += div << bit_pos; + remainder = remainder % divider; + quotient += (uint64_t)div << bit_pos; #ifndef FIXMATH_NO_OVERFLOW if (div & ~(0xFFFFFFFF >> bit_pos)) @@ -375,8 +378,8 @@ fix16_t fix16_div(fix16_t a, fix16_t b) if (b == 0) return fix16_minimum; - uint32_t remainder = (a >= 0) ? a : (-a); - uint32_t divider = (b >= 0) ? b : (-b); + uint32_t remainder = fix_abs(a); + uint32_t divider = fix_abs(b); uint32_t quotient = 0; uint32_t bit = 0x10000; diff --git a/libfixmath/fix16.h b/libfixmath/fix16.h index 7d6b7d3..e378f6b 100644 --- a/libfixmath/fix16.h +++ b/libfixmath/fix16.h @@ -70,9 +70,10 @@ static inline fix16_t fix16_from_float(float a) static inline fix16_t fix16_from_dbl(double a) { double temp = a * fix16_one; -#ifndef FIXMATH_NO_ROUNDING + /* F16() and F16C() are both rounding allways, so this should as well */ +//#ifndef FIXMATH_NO_ROUNDING temp += (double)((temp >= 0) ? 0.5f : -0.5f); -#endif +//#endif return (fix16_t)temp; } @@ -227,6 +228,21 @@ extern void fix16_to_str(fix16_t value, char *buf, int decimals); */ extern fix16_t fix16_from_str(const char *buf); +static inline uint32_t fix_abs(fix16_t in) +{ + if(in == fix16_minimum) + { + // minimum negative number has same representation as + // its absolute value in unsigned + return 0x80000000; + } + else + { + return ((in >= 0)?(in):(-in)); + } +} + + /** Helper macro for F16C. Replace token with its number of characters/digits. */ #define FIXMATH_TOKLEN(token) ( sizeof( #token ) - 1 ) diff --git a/libfixmath/fix16_sqrt.c b/libfixmath/fix16_sqrt.c index 5559b81..f7493e7 100644 --- a/libfixmath/fix16_sqrt.c +++ b/libfixmath/fix16_sqrt.c @@ -11,74 +11,75 @@ */ fix16_t fix16_sqrt(fix16_t inValue) { - uint8_t neg = (inValue < 0); - uint32_t num = (neg ? -inValue : inValue); - uint32_t result = 0; - uint32_t bit; - uint8_t n; - - // Many numbers will be less than 15, so - // this gives a good balance between time spent - // in if vs. time spent in the while loop - // when searching for the starting value. - if (num & 0xFFF00000) - bit = (uint32_t)1 << 30; - else - bit = (uint32_t)1 << 18; - - while (bit > num) bit >>= 2; - - // The main part is executed twice, in order to avoid - // using 64 bit values in computations. - for (n = 0; n < 2; n++) - { - // First we get the top 24 bits of the answer. - while (bit) - { - if (num >= result + bit) - { - num -= result + bit; - result = (result >> 1) + bit; - } - else - { - result = (result >> 1); - } - bit >>= 2; - } - - if (n == 0) - { - // Then process it again to get the lowest 8 bits. - if (num > 65535) - { - // The remainder 'num' is too large to be shifted left - // by 16, so we have to add 1 to result manually and - // adjust 'num' accordingly. - // num = a - (result + 0.5)^2 - // = num + result^2 - (result + 0.5)^2 - // = num - result - 0.5 - num -= result; - num = (num << 16) - 0x8000; - result = (result << 16) + 0x8000; - } - else - { - num <<= 16; - result <<= 16; - } - - bit = 1 << 14; - } - } + uint8_t neg = (inValue < 0); + uint32_t num = fix_abs(inValue); + uint32_t result = 0; + uint32_t bit; + uint8_t n; + + // Many numbers will be less than 15, so + // this gives a good balance between time spent + // in if vs. time spent in the while loop + // when searching for the starting value. + if (num & 0xFFF00000) + bit = (uint32_t)1 << 30; + else + bit = (uint32_t)1 << 18; + + while (bit > num) + bit >>= 2; + + // The main part is executed twice, in order to avoid + // using 64 bit values in computations. + for (n = 0; n < 2; n++) + { + // First we get the top 24 bits of the answer. + while (bit) + { + if (num >= result + bit) + { + num -= result + bit; + result = (result >> 1) + bit; + } + else + { + result = (result >> 1); + } + bit >>= 2; + } + + if (n == 0) + { + // Then process it again to get the lowest 8 bits. + if (num > 65535) + { + // The remainder 'num' is too large to be shifted left + // by 16, so we have to add 1 to result manually and + // adjust 'num' accordingly. + // num = a - (result + 0.5)^2 + // = num + result^2 - (result + 0.5)^2 + // = num - result - 0.5 + num -= result; + num = (num << 16) - 0x8000; + result = (result << 16) + 0x8000; + } + else + { + num <<= 16; + result <<= 16; + } + + bit = 1 << 14; + } + } #ifndef FIXMATH_NO_ROUNDING - // Finally, if next bit would have been 1, round the result upwards. - if (num > result) - { - result++; - } + // Finally, if next bit would have been 1, round the result upwards. + if (num > result) + { + result++; + } #endif - - return (neg ? -(fix16_t)result : (fix16_t)result); + + return (neg ? -(fix16_t)result : (fix16_t)result); } diff --git a/tests/tests.c b/tests/tests.c index cef9710..d06ee26 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -1,7 +1,9 @@ #include "tests.h" #include "tests_basic.h" #include "tests_lerp.h" +#include "tests_macros.h" #include "tests_sqrt.h" +#include "tests_str.h" #include const fix16_t testcases[] = { @@ -32,14 +34,41 @@ const fix16_t testcases[] = { // Tiny random numbers -171, -359, 491, 844, 158, -413, -422, -737, -575, -330, -376, 435, -311, - 116, 715, -1024, -487, 59, 724, 993 -}; + 116, 715, -1024, -487, 59, 724, 993}; unsigned stack_depth = 0; int main() { - printf("\033[1;34m\nVARIANT: \033[39m"STR2(PREFIX)"\033[0m\n"); + printf("\033[1;34m\nVARIANT: \033[39m" STR2(PREFIX) "\033[0m\n"); +#if 0 + fix16_t a = 65536; + fix16_t b = -2147483648; + fix16_t result = fix16_div(a, b); + + double fa = fix16_to_dbl(a); + double fb = fix16_to_dbl(b); + double fresult = fa / fb; + + double max = fix16_to_dbl(fix16_maximum); + double min = fix16_to_dbl(fix16_minimum); + + printf("result %i, %.20f\n", result, fix16_to_dbl(result)); + printf("fresult %i, %.20f\n", fix16_from_dbl(fresult), fresult); + + if ((fa / fb) > max || (fa / fb) < min) + { +#ifndef FIXMATH_NO_OVERFLOW + ASSERT_EQ_INT(result, fix16_overflow); +#endif + } + else + { + ASSERT_NEAR_DOUBLE(fresult, fix16_to_dbl(result), + fix16_to_dbl(fix16_eps), "%i / %i \n", a, b); + } + +#else TEST(test_abs()); TEST(test_add()); TEST(test_mul()); @@ -47,5 +76,8 @@ int main() TEST(test_sub()); TEST(test_sqrt()); TEST(test_lerp()); + TEST(test_macros()); + //TEST(test_str()); +#endif return 0; } diff --git a/tests/tests.cmake b/tests/tests.cmake index 5f6c90b..c07b69d 100644 --- a/tests/tests.cmake +++ b/tests/tests.cmake @@ -15,15 +15,22 @@ set(nn08 PREFIX=nn08 FIXMATH_NO_OVERFLOW FIXMATH_NO_ROUNDING FIXMATH_OPTIMIZE_8B enable_testing() +#-fno-sanitize-recover +set(sanitizer_opts -fsanitize=undefined) + add_custom_target(make_tests) function(create_variant name defs) add_library(libfixmath_${name} STATIC ${libfixmath-srcs}) target_compile_definitions(libfixmath_${name} PRIVATE ${defs}) + target_compile_options(libfixmath_${name} PRIVATE ${sanitizer_opts}) + target_link_options(libfixmath_${name} PRIVATE ${sanitizer_opts}) add_executable(tests_${name} ${tests-srcs}) target_link_libraries(tests_${name} PRIVATE libfixmath_${name} m) target_include_directories(tests_${name} PRIVATE ${CMAKE_SOURCE_DIR}) target_compile_definitions(tests_${name} PRIVATE ${defs}) + target_compile_options(tests_${name} PRIVATE ${sanitizer_opts}) + target_link_options(tests_${name} PRIVATE ${sanitizer_opts}) add_dependencies(make_tests tests_${name}) add_test(NAME tests_${name} COMMAND tests_${name}) endfunction() diff --git a/tests/tests.h b/tests/tests.h index 6bd8e4d..72e7801 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -5,14 +5,15 @@ #include #include #include +#include extern unsigned stack_depth; #define delta(a, b) (((a) >= (b)) ? (a) - (b) : (b) - (a)) #define COMMENT(x) printf("\n----" x "----\n"); -#define STR(x) #x -#define STR2(x) STR(x) +#define STR(x) #x +#define STR2(x) STR(x) #define TEST(x) \ do \ { \ @@ -71,6 +72,34 @@ extern unsigned stack_depth; } \ } while (0) +#define ASSERT_EQ_STR(a, b) \ + do \ + { \ + size_t la = strlen(a); \ + size_t lb = strlen(b); \ + if (la != lb) \ + { \ + fflush(stdout); \ + fflush(stderr); \ + fprintf(stderr, \ + "\033[31;1m FAILED:\033[22;39m%*sASSERT_EQ a: %s, b: " \ + "%s\033[0m at: %s(), " __FILE__ ":" STR2(__LINE__) "\n", \ + stack_depth, "", (a), (b), __func__); \ + return 1; \ + } \ + int res = strncmp((a), (b), la); \ + if (res != 0) \ + { \ + fflush(stdout); \ + fflush(stderr); \ + fprintf(stderr, \ + "\033[31;1m FAILED:\033[22;39m%*sASSERT_EQ a: %s, b: " \ + "%s\033[0m at: %s(), " __FILE__ ":" STR2(__LINE__) "\n", \ + stack_depth, "", (a), (b), __func__); \ + return 1; \ + } \ + } while (0) + extern const fix16_t testcases[102]; #define TESTCASES_COUNT (sizeof(testcases) / sizeof(testcases[0])) diff --git a/tests/tests_basic.c b/tests/tests_basic.c index 6037ae2..8fd3580 100644 --- a/tests/tests_basic.c +++ b/tests/tests_basic.c @@ -5,11 +5,11 @@ int test_abs_short(void) { for (unsigned i = 0; i < TESTCASES_COUNT; ++i) { - fix16_t a = testcases[i]; - double fa = fix16_to_dbl(a); - fix16_t result = fix16_abs(a); - double fresult = fabs(fa); - double min = fix16_to_dbl(fix16_minimum); + fix16_t a = testcases[i]; + double fa = fix16_to_dbl(a); + fix16_t result = fix16_abs(a); + double fresult = fabs(fa); + double min = fix16_to_dbl(fix16_minimum); if (fa <= min) { #ifndef FIXMATH_NO_OVERFLOW diff --git a/tests/tests_macros.c b/tests/tests_macros.c new file mode 100644 index 0000000..bb80c79 --- /dev/null +++ b/tests/tests_macros.c @@ -0,0 +1,100 @@ +#include "tests_macros.h" +#include "tests.h" + +#define DO_TEST(i, m) \ + do \ + { \ + ASSERT_EQ_INT(F16(i##.##m), F16C(i, m)); \ + ASSERT_EQ_INT(F16(i##.##m), fix16_from_dbl(i##.##m)); \ + } while (0) + +int test_macros() +{ + DO_TEST(1, 234); + DO_TEST(0, 0); + DO_TEST(1, 0); + DO_TEST(-1, 0); + DO_TEST(1, 5); + DO_TEST(-1, 5); + DO_TEST(000000, 00000); + DO_TEST(0, 00001); + DO_TEST(0, 00010); + DO_TEST(0, 1); + DO_TEST(0, 10001); + DO_TEST(0, 11000); + DO_TEST(25, 133); + DO_TEST(32767, 00000); + DO_TEST(32767, 00001); + DO_TEST(32767, 99999); + DO_TEST(0, 25); + DO_TEST(0, 99555); + DO_TEST(0, 99998); + DO_TEST(0, 99999); + DO_TEST(-1, 1); + DO_TEST(-25, 133); + DO_TEST(-32767, 00001); + DO_TEST(-32768, 00000); + + /* Random values */ + DO_TEST(0, 02267); + DO_TEST(1, 49887); + DO_TEST(0, 27589); + DO_TEST(0, 38393); + DO_TEST(0, 08934); + DO_TEST(0, 95820); + DO_TEST(0, 95596); + DO_TEST(72, 10642); + DO_TEST(0, 48939); + DO_TEST(3, 37797); + DO_TEST(1, 09194); + DO_TEST(0, 08605); + DO_TEST(3, 04349); + DO_TEST(3, 95401); + DO_TEST(15, 36292); + DO_TEST(56, 09242); + DO_TEST(0, 54071); + DO_TEST(27, 08953); + DO_TEST(0, 03913); + DO_TEST(1, 32707); + DO_TEST(4, 50117); + DO_TEST(0, 24990); + DO_TEST(44, 77319); + DO_TEST(2, 59139); + DO_TEST(0, 16279); + DO_TEST(17, 14712); + DO_TEST(11, 54281); + DO_TEST(0, 02768); + DO_TEST(0, 39278); + DO_TEST(0, 19369); + DO_TEST(-0, 04534); + DO_TEST(-0, 00349); + DO_TEST(-2, 30380); + DO_TEST(-0, 03061); + DO_TEST(-7, 50065); + DO_TEST(-3, 97050); + DO_TEST(-0, 43898); + DO_TEST(-3, 49876); + DO_TEST(-1, 35942); + DO_TEST(-10, 81154); + DO_TEST(-0, 26676); + DO_TEST(-9, 52134); + DO_TEST(-0, 42592); + DO_TEST(-0, 05424); + DO_TEST(-0, 62461); + DO_TEST(-0, 21562); + DO_TEST(-0, 22366); + DO_TEST(-0, 09074); + DO_TEST(-1, 29527); + DO_TEST(-4, 98427); + DO_TEST(-0, 10721); + DO_TEST(-11, 39446); + DO_TEST(-451, 53916); + DO_TEST(-0, 04279); + DO_TEST(-3, 36543); + DO_TEST(-0, 01003); + DO_TEST(-12, 08326); + DO_TEST(-1, 07143); + DO_TEST(-1, 07737); + DO_TEST(-0, 22957); + return 0; +} diff --git a/tests/tests_macros.h b/tests/tests_macros.h new file mode 100644 index 0000000..75ab0f3 --- /dev/null +++ b/tests/tests_macros.h @@ -0,0 +1,6 @@ +#ifndef TESTS_MACROS_H +#define TESTS_MACROS_H + +int test_macros(); + +#endif // TESTS_MACROS_H diff --git a/tests/tests_str.c b/tests/tests_str.c new file mode 100644 index 0000000..d893ffb --- /dev/null +++ b/tests/tests_str.c @@ -0,0 +1,108 @@ +#include "tests_str.h" +#include "tests.h" + +int test_str_to() +{ + char buf[13]; + fix16_to_str(fix16_from_dbl(1234.5678), buf, 4); + ASSERT_EQ_STR(buf, "1234.5678"); + + fix16_to_str(fix16_from_dbl(-1234.5678), buf, 4); + ASSERT_EQ_STR(buf, "-1234.5678"); + + fix16_to_str(0, buf, 0); + ASSERT_EQ_STR(buf, "0"); + + fix16_to_str(fix16_from_dbl(0.9), buf, 0); + ASSERT_EQ_STR(buf, "1"); + + fix16_to_str(1, buf, 5); + ASSERT_EQ_STR(buf, "0.00002"); + + fix16_to_str(-1, buf, 5); + ASSERT_EQ_STR(buf, "-0.00002"); + + fix16_to_str(65535, buf, 5); + ASSERT_EQ_STR(buf, "0.99998"); + + fix16_to_str(65535, buf, 4); + ASSERT_EQ_STR(buf, "1.0000"); + + fix16_to_str(fix16_maximum, buf, 5); + ASSERT_EQ_STR(buf, "32767.99998"); + + fix16_to_str(fix16_minimum, buf, 5); + ASSERT_EQ_STR(buf, "-32768.00000"); + + return 0; +} + +int test_str_from() +{ + ASSERT_EQ_INT(fix16_from_str("1234.5678"), fix16_from_dbl(1234.5678)); + ASSERT_EQ_INT(fix16_from_str("-1234.5678"), fix16_from_dbl(-1234.5678)); + ASSERT_EQ_INT(fix16_from_str(" +1234,56780 "), + fix16_from_dbl(1234.5678)); + + ASSERT_EQ_INT(fix16_from_str("0"), 0); + ASSERT_EQ_INT(fix16_from_str("1"), fix16_one); + ASSERT_EQ_INT(fix16_from_str("1.0"), fix16_one); + ASSERT_EQ_INT(fix16_from_str("1.0000000000"), fix16_one); + + ASSERT_EQ_INT(fix16_from_str("0.00002"), 1); + ASSERT_EQ_INT(fix16_from_str("0.99998"), 65535); + + ASSERT_EQ_INT(fix16_from_str("32767.99998"), fix16_maximum); + ASSERT_EQ_INT(fix16_from_str("-32768.00000"), fix16_minimum); + + return 0; +} + +int test_str_extended() +{ + + fix16_t value = fix16_minimum; + char testbuf[13]; + char goodbuf[13]; + + while (value < fix16_maximum) + { + double fvalue = fix16_to_dbl(value); + + /* Turns out we have to jump through some hoops to round + doubles perfectly for printing: + http://stackoverflow.com/questions/994764/rounding-doubles-5-sprintf + */ + fvalue = round(fvalue * 100000.) / 100000.; + + snprintf(goodbuf, 13, "%0.5f", fvalue); + fix16_to_str(value, testbuf, 5); + + if (strcmp(goodbuf, testbuf) != 0) + { + printf("Value (fix16_t)%d gave %s, should be %s\n", value, testbuf, + goodbuf); + return 1; + } + + fix16_t roundtrip = fix16_from_str(testbuf); + if (roundtrip != value) + { + printf("Roundtrip failed: (fix16_t)%d -> %s -> (fix16_t)%d\n", + value, testbuf, roundtrip); + return 1; + } + + value += 0x10001; + } + + return 0; +} + +int test_str() +{ + TEST(test_str_to()); + TEST(test_str_from()); + TEST(test_str_extended()); + return 0; +} diff --git a/tests/tests_str.h b/tests/tests_str.h new file mode 100644 index 0000000..bb072ea --- /dev/null +++ b/tests/tests_str.h @@ -0,0 +1,6 @@ +#ifndef TESTS_STR_H +#define TESTS_STR_H + +int test_str(); + +#endif // TESTS_STR_H diff --git a/unittests/Makefile b/unittests/Makefile deleted file mode 100644 index 612be42..0000000 --- a/unittests/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -# Makefile for running the unittests of libfixmath. -CC = gcc - -# Basic CFLAGS for debugging -CFLAGS = -g -O0 -I../libfixmath -I.. -Wall -Wextra -Werror - -# The files required for tests -FIX16_SRC = ../libfixmath/fix16.c ../libfixmath/fix16_sqrt.c ../libfixmath/fix16_str.c \ - ../libfixmath/fix16_exp.c ../libfixmath/fix16.h - -all: run_fix16_unittests run_fix16_exp_unittests run_fix16_str_unittests run_fix16_macros_unittests - -clean: - rm -f fix16_unittests_???? - rm -f fix16_str_unittests_default - rm -f fix16_str_unittests_no_ctype - rm -f fix16_exp_unittests - rm -f fix16_macros_unittests - -# The library is tested automatically under different compilations -# options. -# -# Test naming: -# r = rounding, n = no rounding -# o = overflow detection, n = no overflow detection -# 64 = int64_t math, 32 = int32_t math - -run_fix16_unittests: \ - fix16_unittests_ro64 fix16_unittests_no64 \ - fix16_unittests_rn64 fix16_unittests_nn64 \ - fix16_unittests_ro32 fix16_unittests_no32 \ - fix16_unittests_rn32 fix16_unittests_nn32 \ - fix16_unittests_ro08 fix16_unittests_no08 \ - fix16_unittests_rn08 fix16_unittests_nn08 - $(foreach test, $^, \ - echo $(test) && \ - ./$(test) > /dev/null && \ - ) true - -fix16_unittests_no64: DEFINES=-DFIXMATH_NO_ROUNDING -fix16_unittests_rn64: DEFINES=-DFIXMATH_NO_OVERFLOW -fix16_unittests_nn64: DEFINES=-DFIXMATH_NO_ROUNDING -DFIXMATH_NO_OVERFLOW -fix16_unittests_ro32: DEFINES=-DFIXMATH_NO_64BIT -fix16_unittests_no32: DEFINES=-DFIXMATH_NO_ROUNDING -DFIXMATH_NO_64BIT -fix16_unittests_rn32: DEFINES=-DFIXMATH_NO_OVERFLOW -DFIXMATH_NO_64BIT -fix16_unittests_nn32: DEFINES=-DFIXMATH_NO_OVERFLOW -DFIXMATH_NO_ROUNDING -DFIXMATH_NO_64BIT -fix16_unittests_ro08: DEFINES=-DFIXMATH_OPTIMIZE_8BIT -fix16_unittests_no08: DEFINES=-DFIXMATH_NO_ROUNDING -DFIXMATH_OPTIMIZE_8BIT -fix16_unittests_rn08: DEFINES=-DFIXMATH_NO_OVERFLOW -DFIXMATH_OPTIMIZE_8BIT -fix16_unittests_nn08: DEFINES=-DFIXMATH_NO_OVERFLOW -DFIXMATH_NO_ROUNDING -DFIXMATH_OPTIMIZE_8BIT -fix16_str_unittests_no_ctype: DEFINES=-DFIXMATH_NO_CTYPE - -fix16_unittests_% : fix16_unittests.c $(FIX16_SRC) - $(CC) $(CFLAGS) $(DEFINES) -o $@ $^ -lm - - -# Tests for the exponential function, run only in default config -run_fix16_exp_unittests: fix16_exp_unittests - ./fix16_exp_unittests > /dev/null - -fix16_exp_unittests: fix16_exp_unittests.c $(FIX16_SRC) - $(CC) $(CFLAGS) $(DEFINES) -o $@ $^ -lm - -# Tests for string conversion, run only in default config and no ctype -run_fix16_str_unittests: fix16_str_unittests_default fix16_str_unittests_no_ctype - ./fix16_str_unittests_default > /dev/null - ./fix16_str_unittests_no_ctype > /dev/null - -fix16_str_unittests_%: fix16_str_unittests.c $(FIX16_SRC) - $(CC) $(CFLAGS) $(DEFINES) -o $@ $^ -lm - -# Tests for literal macros, run only in default config -run_fix16_macros_unittests: fix16_macros_unittests - ./fix16_macros_unittests > /dev/null - -fix16_macros_unittests: fix16_macros_unittests.c $(FIX16_SRC) - $(CC) $(CFLAGS) $(DEFINES) -o $@ $^ -lm - diff --git a/unittests/fix16_exp_unittests.c b/unittests/fix16_exp_unittests.c deleted file mode 100644 index 9fc0617..0000000 --- a/unittests/fix16_exp_unittests.c +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include -#include -#include "unittests.h" - -#define delta(a,b) (((a)>=(b)) ? (a)-(b) : (b)-(a)) - -int main() -{ - int status = 0; - { - COMMENT("Testing fix16_exp() corner cases"); - TEST(fix16_exp(0) == fix16_one); - TEST(fix16_exp(fix16_minimum) == 0); - TEST(fix16_exp(fix16_maximum) == fix16_maximum); - } - - { - COMMENT("Testing fix16_exp() accuracy over -11..4"); - - fix16_t max_delta = -1; - fix16_t worst = 0; - fix16_t sum = 0; - int count = 0; - fix16_t a; - - for (a = fix16_from_dbl(-11.0); a < fix16_from_dbl(4.0); a += 31) - { - fix16_t result = fix16_exp(a); - fix16_t resultf = fix16_from_dbl(exp(fix16_to_dbl(a))); - - fix16_t d = delta(result, resultf); - if (d > max_delta) - { - max_delta = d; - worst = a; - } - - sum += d; - count++; - } - - printf("Worst delta %d with input %d\n", max_delta, worst); - printf("Average delta %0.2f\n", (float)sum / count); - - TEST(max_delta < 200); - } - - { - COMMENT("Testing fix16_exp() accuracy over full range"); - - float max_delta = -1; - fix16_t worst = 0; - float sum = 0; - int count = 0; - fix16_t a; - - // Test the whole range of results 0..32768 with a bit less samples - for (a = -772243; a < 681391; a += 113) - { - fix16_t result = fix16_exp(a); - fix16_t resultf = fix16_from_dbl(exp(fix16_to_dbl(a))); - - fix16_t d1 = delta(result, resultf); - - if (d1 > 0) d1--; // Forgive +-1 for the fix16_t inaccuracy - - float d = (float)d1 / resultf * 100; - - if (resultf < 1000) continue; // Percentages can explode when result is almost 0. - - if (d > max_delta) - { - max_delta = d; - worst = a; - } - - sum += d; - count++; - } - - printf("Worst delta %0.4f%% with input %d\n", max_delta, worst); - printf("Average delta %0.4f%%\n", sum / count); - - TEST(max_delta < 1); - } - - { - COMMENT("Testing fix16_log() accuracy over full range"); - - fix16_t max_delta = -1; - fix16_t worst = 0; - fix16_t sum = 0; - int count = 0; - fix16_t a; - - for (a = 100; a > 0 && a < fix16_maximum - 7561; a += 7561) - { - fix16_t result = fix16_log(a); - fix16_t resultf = fix16_from_dbl(log(fix16_to_dbl(a))); - - fix16_t d = delta(result, resultf); - if (d > max_delta) - { - max_delta = d; - worst = a; - } - - sum += d; - count++; - } - - printf("Worst delta %d with input %d\n", max_delta, worst); - printf("Average delta %0.2f\n", (float)sum / count); - - TEST(max_delta < 20); - } - - if (status != 0) - fprintf(stdout, "\n\nSome tests FAILED!\n"); - - return status; -} diff --git a/unittests/fix16_macros_unittests.c b/unittests/fix16_macros_unittests.c deleted file mode 100644 index e961d58..0000000 --- a/unittests/fix16_macros_unittests.c +++ /dev/null @@ -1,110 +0,0 @@ -/* This test checks that the F16() and F16C() macros work correctly. */ - -#include -#include -#include -#include -#include "unittests.h" - -#define DO_TEST(i,m) \ - TEST(F16(i ## . ## m) == F16C(i,m)) \ - TEST(F16(i ## . ## m) == fix16_from_dbl(i ## . ## m)) - -int main() -{ - int status = 0; - - /* Corner cases */ - DO_TEST(1,234) - DO_TEST(0,0) - DO_TEST(1,0) - DO_TEST(-1,0) - DO_TEST(1,5) - DO_TEST(-1,5) - DO_TEST(000000,00000) - DO_TEST(0,00001) - DO_TEST(0,00010) - DO_TEST(0,1) - DO_TEST(0,10001) - DO_TEST(0,11000) - DO_TEST(25,133) - DO_TEST(32767,00000) - DO_TEST(32767,00001) - DO_TEST(32767,99999) - DO_TEST(0,25) - DO_TEST(0,99555) - DO_TEST(0,99998) - DO_TEST(0,99999) - DO_TEST(-1,1) - DO_TEST(-25,133) - DO_TEST(-32767,00001) - DO_TEST(-32768,00000) - - /* Random values */ - DO_TEST( 0,02267) - DO_TEST( 1,49887) - DO_TEST( 0,27589) - DO_TEST( 0,38393) - DO_TEST( 0,08934) - DO_TEST( 0,95820) - DO_TEST( 0,95596) - DO_TEST( 72,10642) - DO_TEST( 0,48939) - DO_TEST( 3,37797) - DO_TEST( 1,09194) - DO_TEST( 0,08605) - DO_TEST( 3,04349) - DO_TEST( 3,95401) - DO_TEST( 15,36292) - DO_TEST( 56,09242) - DO_TEST( 0,54071) - DO_TEST( 27,08953) - DO_TEST( 0,03913) - DO_TEST( 1,32707) - DO_TEST( 4,50117) - DO_TEST( 0,24990) - DO_TEST( 44,77319) - DO_TEST( 2,59139) - DO_TEST( 0,16279) - DO_TEST( 17,14712) - DO_TEST( 11,54281) - DO_TEST( 0,02768) - DO_TEST( 0,39278) - DO_TEST( 0,19369) - DO_TEST( -0,04534) - DO_TEST( -0,00349) - DO_TEST( -2,30380) - DO_TEST( -0,03061) - DO_TEST( -7,50065) - DO_TEST( -3,97050) - DO_TEST( -0,43898) - DO_TEST( -3,49876) - DO_TEST( -1,35942) - DO_TEST( -10,81154) - DO_TEST( -0,26676) - DO_TEST( -9,52134) - DO_TEST( -0,42592) - DO_TEST( -0,05424) - DO_TEST( -0,62461) - DO_TEST( -0,21562) - DO_TEST( -0,22366) - DO_TEST( -0,09074) - DO_TEST( -1,29527) - DO_TEST( -4,98427) - DO_TEST( -0,10721) - DO_TEST( -11,39446) - DO_TEST(-451,53916) - DO_TEST( -0,04279) - DO_TEST( -3,36543) - DO_TEST( -0,01003) - DO_TEST( -12,08326) - DO_TEST( -1,07143) - DO_TEST( -1,07737) - DO_TEST( -0,22957) - - if (status != 0) - fprintf(stdout, "\n\nSome tests FAILED!\n"); - - return status; -} - diff --git a/unittests/fix16_str_unittests.c b/unittests/fix16_str_unittests.c deleted file mode 100644 index 1135eb8..0000000 --- a/unittests/fix16_str_unittests.c +++ /dev/null @@ -1,117 +0,0 @@ -#include -#include -#include -#include -#include -#include "unittests.h" - -int main() -{ - int status = 0; - - { - COMMENT("Testing fix16_to_str corner cases"); - char buf[13]; - - fix16_to_str(fix16_from_dbl(1234.5678), buf, 4); - printf("1234.5678 = %s\n", buf); - TEST(strcmp(buf, "1234.5678") == 0); - - fix16_to_str(fix16_from_dbl(-1234.5678), buf, 4); - printf("-1234.5678 = %s\n", buf); - TEST(strcmp(buf, "-1234.5678") == 0); - - fix16_to_str(0, buf, 0); - TEST(strcmp(buf, "0") == 0); - - fix16_to_str(fix16_from_dbl(0.9), buf, 0); - TEST(strcmp(buf, "1") == 0); - - fix16_to_str(1, buf, 5); - printf("(fix16_t)1 = %s\n", buf); - TEST(strcmp(buf, "0.00002") == 0); - - fix16_to_str(-1, buf, 5); - printf("(fix16_t)-1 = %s\n", buf); - TEST(strcmp(buf, "-0.00002") == 0); - - fix16_to_str(65535, buf, 5); - printf("(fix16_t)65535 = %s\n", buf); - TEST(strcmp(buf, "0.99998") == 0); - - fix16_to_str(65535, buf, 4); - printf("(fix16_t)65535 = %s\n", buf); - TEST(strcmp(buf, "1.0000") == 0); - - fix16_to_str(fix16_maximum, buf, 5); - printf("fix16_maximum = %s\n", buf); - TEST(strcmp(buf, "32767.99998") == 0); - - fix16_to_str(fix16_minimum, buf, 5); - printf("fix16_minimum = %s\n", buf); - TEST(strcmp(buf, "-32768.00000") == 0); - } - - { - COMMENT("Testing fix16_from_str corner cases"); - - TEST(fix16_from_str("1234.5678") == fix16_from_dbl(1234.5678)); - TEST(fix16_from_str("-1234.5678") == fix16_from_dbl(-1234.5678)); - TEST(fix16_from_str(" +1234,56780 ") == fix16_from_dbl(1234.5678)); - - TEST(fix16_from_str("0") == 0); - TEST(fix16_from_str("1") == fix16_one); - TEST(fix16_from_str("1.0") == fix16_one); - TEST(fix16_from_str("1.0000000000") == fix16_one); - - TEST(fix16_from_str("0.00002") == 1); - TEST(fix16_from_str("0.99998") == 65535); - - TEST(fix16_from_str("32767.99998") == fix16_maximum); - TEST(fix16_from_str("-32768.00000") == fix16_minimum); - } - - { - COMMENT("Extended testing for whole range"); - fix16_t value = fix16_minimum; - char testbuf[13]; - char goodbuf[13]; - - bool ok = true; - while (value < fix16_maximum) - { - double fvalue = fix16_to_dbl(value); - - /* Turns out we have to jump through some hoops to round - doubles perfectly for printing: - http://stackoverflow.com/questions/994764/rounding-doubles-5-sprintf - */ - fvalue = round(fvalue * 100000.)/100000.; - - snprintf(goodbuf, 13, "%0.5f", fvalue); - fix16_to_str(value, testbuf, 5); - - if (strcmp(goodbuf, testbuf) != 0) - { - printf("Value (fix16_t)%d gave %s, should be %s\n", value, testbuf, goodbuf); - ok = false; - } - - fix16_t roundtrip = fix16_from_str(testbuf); - if (roundtrip != value) - { - printf("Roundtrip failed: (fix16_t)%d -> %s -> (fix16_t)%d\n", value, testbuf, roundtrip); - ok = false; - } - - value += 0x10001; - } - - TEST(ok); - } - - if (status != 0) - fprintf(stdout, "\n\nSome tests FAILED!\n"); - - return status; -} diff --git a/unittests/fix16_unittests.c b/unittests/fix16_unittests.c deleted file mode 100644 index 436c979..0000000 --- a/unittests/fix16_unittests.c +++ /dev/null @@ -1,391 +0,0 @@ -#include -#include -#include -#include -#include "libfixmath/int64.h" -#include "unittests.h" - -const fix16_t testcases[] = { - // Small numbers - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, - - // Integer numbers - 0x10000, -0x10000, 0x20000, -0x20000, 0x30000, -0x30000, - 0x40000, -0x40000, 0x50000, -0x50000, 0x60000, -0x60000, - - // Fractions (1/2, 1/4, 1/8) - 0x8000, -0x8000, 0x4000, -0x4000, 0x2000, -0x2000, - - // Problematic carry - 0xFFFF, -0xFFFF, 0x1FFFF, -0x1FFFF, 0x3FFFF, -0x3FFFF, - - // Smallest and largest values - 0x7FFFFFFF, 0x80000000, - - // Large random numbers - 831858892, 574794913, 2147272293, -469161054, -961611615, - 1841960234, 1992698389, 520485404, 560523116, -2094993050, - -876897543, -67813629, 2146227091, 509861939, -1073573657, - - // Small random numbers - -14985, 30520, -83587, 41129, 42137, 58537, -2259, 84142, - -28283, 90914, 19865, 33191, 81844, -66273, -63215, -44459, - -11326, 84295, 47515, -39324, - - // Tiny random numbers - -171, -359, 491, 844, 158, -413, -422, -737, -575, -330, - -376, 435, -311, 116, 715, -1024, -487, 59, 724, 993 -}; - -#define TESTCASES_COUNT (sizeof(testcases)/sizeof(testcases[0])) - -#define delta(a,b) (((a)>=(b)) ? (a)-(b) : (b)-(a)) - -#ifdef FIXMATH_NO_ROUNDING -const fix16_t max_delta = 1; -#else -const fix16_t max_delta = 0; -#endif - -int main() -{ - int status = 0; - - { - COMMENT("Testing basic multiplication"); - TEST(fix16_mul(fix16_from_int(5), fix16_from_int(5)) == fix16_from_int(25)); - TEST(fix16_mul(fix16_from_int(-5), fix16_from_int(5)) == fix16_from_int(-25)); - TEST(fix16_mul(fix16_from_int(-5), fix16_from_int(-5)) == fix16_from_int(25)); - TEST(fix16_mul(fix16_from_int(5), fix16_from_int(-5)) == fix16_from_int(-25)); - } - -#ifndef FIXMATH_NO_ROUNDING - { - COMMENT("Testing multiplication rounding corner cases"); - TEST(fix16_mul(0, 10) == 0); - TEST(fix16_mul(2, 0x8000) == 1); - TEST(fix16_mul(-2, 0x8000) == -1); - TEST(fix16_mul(3, 0x8000) == 2); - TEST(fix16_mul(-3, 0x8000) == -2); - TEST(fix16_mul(2, 0x7FFF) == 1); - TEST(fix16_mul(-2, 0x7FFF) == -1); - TEST(fix16_mul(2, 0x8001) == 1); - TEST(fix16_mul(-2, 0x8001) == -1); - } -#endif - - { - unsigned int i, j; - int failures = 0; - COMMENT("Running testcases for multiplication"); - - for (i = 0; i < TESTCASES_COUNT; i++) - { - for (j = 0; j < TESTCASES_COUNT; j++) - { - fix16_t a = testcases[i]; - fix16_t b = testcases[j]; - fix16_t result = fix16_mul(a, b); - - double fa = fix16_to_dbl(a); - double fb = fix16_to_dbl(b); - fix16_t fresult = fix16_from_dbl(fa * fb); - - double max = fix16_to_dbl(fix16_maximum); - double min = fix16_to_dbl(fix16_minimum); - - if (delta(fresult, result) > max_delta) - { - if (fa * fb > max || fa * fb < min) - { - #ifndef FIXMATH_NO_OVERFLOW - if (result != fix16_overflow) - { - printf("\n%d * %d overflow not detected!\n", a, b); - failures++; - } - #endif - // Legitimate overflow - continue; - } - - printf("\n%d * %d = %d\n", a, b, result); - printf("%f * %f = %d\n", fa, fb, fresult); - failures++; - } - } - } - - TEST(failures == 0); - } - - { - COMMENT("Testing basic division"); - TEST(fix16_div(fix16_from_int(15), fix16_from_int(5)) == fix16_from_int(3)); - TEST(fix16_div(fix16_from_int(-15), fix16_from_int(5)) == fix16_from_int(-3)); - TEST(fix16_div(fix16_from_int(-15), fix16_from_int(-5)) == fix16_from_int(3)); - TEST(fix16_div(fix16_from_int(15), fix16_from_int(-5)) == fix16_from_int(-3)); - } - -#ifndef FIXMATH_NO_ROUNDING - { - COMMENT("Testing division rounding corner cases"); - TEST(fix16_div(0, 10) == 0); - TEST(fix16_div(1, fix16_from_int(2)) == 1); - TEST(fix16_div(-1, fix16_from_int(2)) == -1); - TEST(fix16_div(1, fix16_from_int(-2)) == -1); - TEST(fix16_div(-1, fix16_from_int(-2)) == 1); - TEST(fix16_div(3, fix16_from_int(2)) == 2); - TEST(fix16_div(-3, fix16_from_int(2)) == -2); - TEST(fix16_div(3, fix16_from_int(-2)) == -2); - TEST(fix16_div(-3, fix16_from_int(-2)) == 2); - TEST(fix16_div(2, 0x7FFF) == 4); - TEST(fix16_div(-2, 0x7FFF) == -4); - TEST(fix16_div(2, 0x8001) == 4); - TEST(fix16_div(-2, 0x8001) == -4); - } -#endif - - { - unsigned int i, j; - int failures = 0; - COMMENT("Running testcases for division"); - - for (i = 0; i < TESTCASES_COUNT; i++) - { - for (j = 0; j < TESTCASES_COUNT; j++) - { - fix16_t a = testcases[i]; - fix16_t b = testcases[j]; - - // We don't require a solution for /0 :) - if (b == 0) continue; - - fix16_t result = fix16_div(a, b); - - double fa = fix16_to_dbl(a); - double fb = fix16_to_dbl(b); - fix16_t fresult = fix16_from_dbl(fa / fb); - - double max = fix16_to_dbl(fix16_maximum); - double min = fix16_to_dbl(fix16_minimum); - if (delta(fresult, result) > max_delta) - { - if (((fa / fb) > max) || ((fa / fb) < min)) - { - #ifndef FIXMATH_NO_OVERFLOW - if (result != fix16_overflow) - { - printf("\n%d / %d overflow not detected!\n", a, b); - failures++; - } - #endif - // Legitimate overflow - continue; - } - - printf("\n%f / %f = %f\n", fix16_to_dbl(a), fix16_to_dbl(b), fix16_to_dbl(fresult)); - printf("%f / %f = %f\n", fa, fb, (fa / fb)); - failures++; - } - } - } - - TEST(failures == 0); - } - - { - unsigned int i, j; - int failures = 0; - COMMENT("Running testcases for addition"); - - for (i = 0; i < TESTCASES_COUNT; i++) - { - for (j = 0; j < TESTCASES_COUNT; j++) - { - fix16_t a = testcases[i]; - fix16_t b = testcases[j]; - - fix16_t result = fix16_add(a, b); - - double fa = fix16_to_dbl(a); - double fb = fix16_to_dbl(b); - fix16_t fresult = fix16_from_dbl(fa + fb); - - double max = fix16_to_dbl(fix16_maximum); - double min = fix16_to_dbl(fix16_minimum); - - if (delta(fresult, result) > max_delta) - { - if (fa + fb > max || fa + fb < min) - { - #ifndef FIXMATH_NO_OVERFLOW - if (result != fix16_overflow) - { - printf("\n%d + %d overflow not detected!\n", a, b); - failures++; - } - #endif - // Legitimate overflow - continue; - } - - printf("\n%d + %d = %d\n", a, b, result); - printf("%f + %f = %d\n", fa, fb, fresult); - failures++; - } - } - } - - TEST(failures == 0); - } - - { - unsigned int i, j; - int failures = 0; - COMMENT("Running testcases for subtraction"); - - for (i = 0; i < TESTCASES_COUNT; i++) - { - for (j = 0; j < TESTCASES_COUNT; j++) - { - fix16_t a = testcases[i]; - fix16_t b = testcases[j]; - - fix16_t result = fix16_sub(a, b); - - double fa = fix16_to_dbl(a); - double fb = fix16_to_dbl(b); - fix16_t fresult = fix16_from_dbl(fa - fb); - - double max = fix16_to_dbl(fix16_maximum); - double min = fix16_to_dbl(fix16_minimum); - - if (delta(fresult, result) > max_delta) - { - if (fa - fb > max || fa - fb < min) - { - #ifndef FIXMATH_NO_OVERFLOW - if (result != fix16_overflow) - { - printf("\n%d - %d overflow not detected!\n", a, b); - failures++; - } - #endif - // Legitimate overflow - continue; - } - - printf("\n%d - %d = %d\n", a, b, result); - printf("%f - %f = %d\n", fa, fb, fresult); - failures++; - } - } - } - - TEST(failures == 0); - } - - { - COMMENT("Testing basic square roots"); - TEST(fix16_sqrt(fix16_from_int(16)) == fix16_from_int(4)); - TEST(fix16_sqrt(fix16_from_int(100)) == fix16_from_int(10)); - TEST(fix16_sqrt(fix16_from_int(1)) == fix16_from_int(1)); - } - -#ifndef FIXMATH_NO_ROUNDING - { - COMMENT("Testing square root rounding corner cases"); - TEST(fix16_sqrt(214748302) == 3751499); - TEST(fix16_sqrt(214748303) == 3751499); - TEST(fix16_sqrt(214748359) == 3751499); - TEST(fix16_sqrt(214748360) == 3751500); - } -#endif - - { - unsigned int i; - int failures = 0; - COMMENT("Running test cases for square root"); - - for (i = 0; i < TESTCASES_COUNT; i++) - { - fix16_t a = testcases[i]; - - if (a < 0) continue; - - fix16_t result = fix16_sqrt(a); - - double fa = fix16_to_dbl(a); - fix16_t fresult = fix16_from_dbl(sqrt(fa)); - - if (delta(fresult, result) > max_delta) - { - printf("\nfix16_sqrt(%d) = %d\n", a, result); - printf("sqrt(%f) = %d\n", fa, fresult); - failures++; - } - } - - TEST(failures == 0); - } - - { - COMMENT("Running int64 test cases"); - TEST(int64_cmp_eq(int64_const(0,1), int64_from_int32(1))); - TEST(int64_cmp_eq(int64_const(0xffffffff,0xfffffffe), int64_from_int32(-2))); - TEST(int64_cmp_eq(int64_const(1,0), int64_shift(int64_from_int32(1),32))); - TEST(int64_cmp_eq(int64_const(1,0), int64_shift(int64_from_int32(2),31))); - TEST(int64_cmp_eq(int64_const(0,(1<<31)), int64_shift(int64_from_int32(1),31))); - TEST(int64_cmp_eq(int64_const(-2,0), int64_shift(int64_from_int32(-1),33))); - TEST(int64_cmp_eq(int64_const(0,1), int64_shift(int64_const(0,2),-1))); - TEST(int64_cmp_eq(int64_const(2,1), int64_shift(int64_const(4,2),-1))); - TEST(int64_cmp_eq(int64_const(0,(1<<31)), int64_shift(int64_const(1,0),-1))); - TEST(int64_cmp_eq(int64_const(0,4), int64_shift(int64_const(2,0),-31))); - TEST(int64_cmp_eq(int64_const(0,2), int64_shift(int64_const(2,0),-32))); - TEST(int64_cmp_eq(int64_const(0,1), int64_shift(int64_const(2,0),-33))); - int64_t bit31 = int64_const(0, 0x80000000); - int64_t negbit31 = int64_const(-1, 0x80000000); - TEST(int64_cmp_eq(negbit31, int64_neg(bit31))); - TEST(int64_cmp_eq(int64_const(1,0), int64_mul_i64_i32(bit31, 2))); - TEST(int64_cmp_eq(int64_const(-1,(1<<31)), int64_mul_i64_i32(bit31, -1))); - TEST(int64_cmp_eq(int64_mul_i64_i32(int64_const(0,-1), fix16_maximum), - int64_const(0x7ffffffe, 0x80000001))); - TEST(int64_cmp_eq(int64_mul_i64_i32(int64_const(0,1), fix16_minimum), - int64_const(-1, fix16_minimum))); - } - - { - COMMENT("Running linear interpolation test cases"); - - TEST(fix16_lerp8(0, 2, 0) == 0); - TEST(fix16_lerp8(0, 2, 127) == 0); - TEST(fix16_lerp8(0, 2, 128) == 1); - TEST(fix16_lerp8(0, 2, 255) == 1); - TEST(fix16_lerp8(fix16_minimum, fix16_maximum, 0) == fix16_minimum); - TEST(fix16_lerp8(fix16_minimum, fix16_maximum, 255) == (fix16_maximum - (1<<24))); - TEST(fix16_lerp8(-fix16_maximum, fix16_maximum, 128) == 0); - - TEST(fix16_lerp16(0, 2, 0) == 0); - TEST(fix16_lerp16(0, 2, 0x7fff) == 0); - TEST(fix16_lerp16(0, 2, 0x8000) == 1); - TEST(fix16_lerp16(0, 2, 0xffff) == 1); - TEST(fix16_lerp16(fix16_minimum, fix16_maximum, 0) == fix16_minimum); - TEST(fix16_lerp16(fix16_minimum, fix16_maximum, 0xffff) == (fix16_maximum - (1<<16))); - TEST(fix16_lerp16(-fix16_maximum, fix16_maximum, 0x8000) == 0); - - TEST(fix16_lerp32(0, 2, 0) == 0); - TEST(fix16_lerp32(0, 2, 0x7fffffff) == 0); - TEST(fix16_lerp32(0, 2, 0x80000000) == 1); - TEST(fix16_lerp32(0, 2, 0xffffffff) == 1); - TEST(fix16_lerp32(fix16_minimum, fix16_maximum, 0) == fix16_minimum); - TEST(fix16_lerp32(fix16_minimum, fix16_maximum, 0xffffffff) == (fix16_maximum - 1)); - TEST(fix16_lerp32(-fix16_maximum, fix16_maximum, 0x80000000) == 0); - } - - if (status != 0) - fprintf(stdout, "\n\nSome tests FAILED!\n"); - - return status; -} diff --git a/unittests/run_unittests.sh b/unittests/run_unittests.sh deleted file mode 100644 index 1fc1469..0000000 --- a/unittests/run_unittests.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -fail=0 -pass=0 - -for f in fix16_unittests_* fix16_exp_unittests fix16_macros_unittests fix16_str_unittests_* -do - if ! ./$f >> /dev/null; then - echo "./$f failed!" - ((fail=fail+1)) - else - ((pass=pass+1)) - fi -done - -echo "$fail tests failed, $pass tests passed." \ No newline at end of file diff --git a/unittests/unittests.h b/unittests/unittests.h deleted file mode 100644 index bac57d2..0000000 --- a/unittests/unittests.h +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#define COMMENT(x) printf("\n----" x "----\n"); -#define STR(x) #x -#define STR2(x) STR(x) -#define TEST(x) \ - if (!(x)) { \ - fflush(stdout); \ - fflush(stderr); \ - fprintf(stderr, "\033[31;1mFAILED:\033[22;39m " __FILE__ ":" STR2(__LINE__) " " #x "\n"); \ - status = 1; \ - } else { \ - fflush(stdout); \ - fflush(stderr); \ - printf("\033[32;1mOK:\033[22;39m " #x "\n"); \ - } - -