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 0000000..29a0894 Binary files /dev/null and b/benchmarks/results-avr.ods differ 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"); \ - } - -