diff options
| author | Petteri.Aimonen <Petteri.Aimonen@gmail.com> | 2012-01-26 15:43:30 +0000 |
|---|---|---|
| committer | Petteri.Aimonen <Petteri.Aimonen@gmail.com> | 2012-01-26 15:43:30 +0000 |
| commit | 1076285d2655b82a4d19cbd46af305ea48fe7c52 (patch) | |
| tree | 52b8f5423707b40881ada210a123a72ad552fffa /unittests | |
| parent | 681fd5aea1c1b6fb618e6c7902cf70e417d8233c (diff) | |
| download | libfixmath-1076285d2655b82a4d19cbd46af305ea48fe7c52.tar.gz | |
Merging a bunch of separately developed functions:
fix16_mul, fix16_div, fix16_sqrt.
They are faster & more accurate than the previous versions.
Closes issue #13.
Includes unittests for the functions in question, runnable
by typing 'make' in the unittests folder.
Diffstat (limited to 'unittests')
| -rw-r--r-- | unittests/Makefile | 49 | ||||
| -rw-r--r-- | unittests/fix16_unittests.c | 337 | ||||
| -rw-r--r-- | unittests/unittests.h | 18 |
3 files changed, 404 insertions, 0 deletions
diff --git a/unittests/Makefile b/unittests/Makefile new file mode 100644 index 0000000..80df66b --- /dev/null +++ b/unittests/Makefile @@ -0,0 +1,49 @@ +# Makefile for running the unittests of libfixmath. +CC = gcc + +# Basic CFLAGS for debugging +CFLAGS = -g -O0 -I../libfixmath -Wall -Wextra -Werror + +# The files required for tests +FIX16_SRC = ../libfixmath/fix16.c ../libfixmath/fix16_sqrt.c \ + ../libfixmath/fix16.h + +all: run_fix16_unittests + +clean: + rm -f fix16_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_unittests_% : fix16_unittests.c $(FIX16_SRC) + $(CC) $(CFLAGS) $(DEFINES) -o $@ $^ -lm diff --git a/unittests/fix16_unittests.c b/unittests/fix16_unittests.c new file mode 100644 index 0000000..a47cfb6 --- /dev/null +++ b/unittests/fix16_unittests.c @@ -0,0 +1,337 @@ +#include <fix16.h> +#include <stdio.h> +#include <math.h> +#include <stdbool.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_max); + double min = fix16_to_dbl(fix16_min); + + 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_max); + double min = fix16_to_dbl(fix16_min); + 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 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_max); + double min = fix16_to_dbl(fix16_min); + + 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_max); + double min = fix16_to_dbl(fix16_min); + + 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); + } + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +} diff --git a/unittests/unittests.h b/unittests/unittests.h new file mode 100644 index 0000000..bac57d2 --- /dev/null +++ b/unittests/unittests.h @@ -0,0 +1,18 @@ +#include <stdio.h> + +#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"); \ + } + + |
