aboutsummaryrefslogtreecommitdiff
path: root/unittests
diff options
context:
space:
mode:
authorPetteri.Aimonen <Petteri.Aimonen@gmail.com>2012-01-26 15:43:30 +0000
committerPetteri.Aimonen <Petteri.Aimonen@gmail.com>2012-01-26 15:43:30 +0000
commit1076285d2655b82a4d19cbd46af305ea48fe7c52 (patch)
tree52b8f5423707b40881ada210a123a72ad552fffa /unittests
parent681fd5aea1c1b6fb618e6c7902cf70e417d8233c (diff)
downloadlibfixmath-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/Makefile49
-rw-r--r--unittests/fix16_unittests.c337
-rw-r--r--unittests/unittests.h18
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"); \
+ }
+
+