From 35eb7753d29a9ad14b145a09c057a88131d6ede0 Mon Sep 17 00:00:00 2001 From: Martin Baƙinka Date: Thu, 29 Apr 2021 08:40:06 +0200 Subject: New unittests (#28) Refactor unittests --- .gitignore | 4 + CMakeLists.txt | 22 ++-- benchmarks/Makefile | 5 +- contrib/fix16_fft.c | 163 -------------------------- libfixmath/fix16.h | 1 + libfixmath/fix16_fft.c | 163 ++++++++++++++++++++++++++ libfixmath/libfixmath.cmake | 3 + tests/run_tests | 2 + tests/tests.c | 51 +++++++++ tests/tests.cmake | 44 +++++++ tests/tests.h | 80 +++++++++++++ tests/tests_basic.c | 246 ++++++++++++++++++++++++++++++++++++++++ tests/tests_basic.h | 10 ++ tests/tests_lerp.c | 33 ++++++ tests/tests_lerp.h | 6 + tests/tests_sqrt.c | 37 ++++++ tests/tests_sqrt.h | 6 + unittests/Makefile | 2 +- unittests/fix16_exp_unittests.c | 2 +- unittests/fix16_unittests.c | 4 +- 20 files changed, 707 insertions(+), 177 deletions(-) delete mode 100644 contrib/fix16_fft.c create mode 100644 libfixmath/fix16_fft.c create mode 100644 libfixmath/libfixmath.cmake create mode 100755 tests/run_tests create mode 100644 tests/tests.c create mode 100644 tests/tests.cmake create mode 100644 tests/tests.h create mode 100644 tests/tests_basic.c create mode 100644 tests/tests_basic.h create mode 100644 tests/tests_lerp.c create mode 100644 tests/tests_lerp.h create mode 100644 tests/tests_sqrt.c create mode 100644 tests/tests_sqrt.h diff --git a/.gitignore b/.gitignore index a6cb559..59904ca 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,7 @@ compile_commands.json *creator.user* *_qmlcache.qrc + +benchmarks/testcases.c + +*.elf diff --git a/CMakeLists.txt b/CMakeLists.txt index 75b7322..204ef3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,25 +4,29 @@ project(libfixmath LANGUAGES CXX C) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_C_FLAGS "-Wall -pedantic -Wextra") +set(CMAKE_C_FLAGS "-Wall -pedantic -Wextra -Werror=return-type") -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS "-Wall -pedantic -Wextra") +set(CMAKE_CXX_FLAGS "-Wall -pedantic -Wextra -Werror=return-type") + +include(libfixmath/libfixmath.cmake) +include(tests/tests.cmake) -file(GLOB libfixmath-srcs libfixmath/*.h libfixmath/*.hpp libfixmath/*.c) -file(GLOB contrib-srcs contrib/*.c) file(GLOB fixsingen-srcs fixsingen/*.c) file(GLOB fixtest-srcs fixtest/*.c fixtest/*.h) - -add_library(libfixmath STATIC ${libfixmath-srcs} ${contrib-srcs}) -target_include_directories(libfixmath PRIVATE ${CMAKE_SOURCE_DIR}) +file(GLOB unittests-srcs unittests/*.c unittests/*.h) add_executable(fixtest ${fixtest-srcs}) target_link_libraries(fixtest PRIVATE libfixmath m) target_include_directories(fixtest PRIVATE ${CMAKE_SOURCE_DIR}) - 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 efd450e..6331596 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -6,6 +6,9 @@ FILES = benchmark.c ../libfixmath/fix16.c ../libfixmath/fix16_sqrt.c ../libfixma CFLAGS = -DFIXMATH_NO_OVERFLOW -DFIXMATH_NO_ROUNDING -ffast-math -I../libfixmath +.PHONY clean: + rm -f *.elf + testcases.c: generate_testcases.py python $< @@ -23,7 +26,7 @@ run-benchmark-arm: benchmark-arm.elf benchmark-avr.elf: $(FILES) interface-avr.c testcases.c avr-gcc -Wall -mmcu=atmega128 $(CFLAGS) \ - -Wall -O2 -DFIXMATH_OPTIMIZE_8BIT \ + -O2 -DFIXMATH_OPTIMIZE_8BIT \ -o $@ -I .. $(FILES) interface-avr.c run-benchmark-avr: benchmark-avr.elf diff --git a/contrib/fix16_fft.c b/contrib/fix16_fft.c deleted file mode 100644 index 8259083..0000000 --- a/contrib/fix16_fft.c +++ /dev/null @@ -1,163 +0,0 @@ -/* Real-input FFT implementation using the libfixmath fix16_t datatype. - * Not the fastest implementation ever, but has a small code size. - * - * Refer to http://www.dspguide.com/ch12/2.htm for information on the - * algorithm. - * - * (c) 2012 Petteri Aimonen - * This file is released to public domain. - */ - -#include -#include - -// You can change the input datatype and intermediate scaling here. -// By default, the output is divided by the transform length to get a normalized FFT. -// Input_convert determines the scaling of intermediate values. Multiplication by 256 -// gives a nice compromise between precision and numeric range. -#ifndef INPUT_TYPE -#define INPUT_TYPE uint8_t -#endif - -#ifndef INPUT_CONVERT -#define INPUT_CONVERT(x) ((x) << 8) -#endif - -#ifndef INPUT_INDEX -#define INPUT_INDEX(x) (x) -#endif - -#ifndef OUTPUT_SCALE -#define OUTPUT_SCALE(transform_size) (fix16_one * 256 / transform_size) -#endif - -// Fast calculation of DFT for a 4-point signal. Based on the simplicity -// of 4-point sinewave. -static void four_point_dft(INPUT_TYPE *input, unsigned input_stride, - fix16_t *real, fix16_t *imag) -{ - fix16_t x0 = INPUT_CONVERT(input[0 * input_stride]); - fix16_t x1 = INPUT_CONVERT(input[1 * input_stride]); - fix16_t x2 = INPUT_CONVERT(input[2 * input_stride]); - fix16_t x3 = INPUT_CONVERT(input[3 * input_stride]); - - real[0] = x0 + x1 + x2 + x3; - imag[0] = 0; - real[1] = x0 - x2; - imag[1] = -x1 + x3; - real[2] = x0 - x1 + x2 - x3; - imag[2] = 0; - real[3] = x0 - x2; - imag[3] = x1 - x3; -} - -// Mix N blocksize-sized transforms pairwise together to get N/2 2*blocksize-sized transforms. -static void butterfly(fix16_t *real, fix16_t *imag, unsigned blocksize, unsigned blockpairs) -{ - unsigned i, j; - for (i = 0; i < blocksize; i++) - { - fix16_t angle = fix16_pi * i / blocksize; - fix16_t c = fix16_cos(angle); - fix16_t s = -fix16_sin(angle); - - fix16_t *rp = real + i; - fix16_t *ip = imag + i; - for (j = 0; j < blockpairs; j++) - { - // Get the odd-indexed tranform and multiply by sine - fix16_t re = fix16_mul(rp[blocksize], c) - fix16_mul(ip[blocksize], s); - fix16_t im = fix16_mul(ip[blocksize], c) + fix16_mul(rp[blocksize], s); - - // Update the transforms - rp[blocksize] = rp[0] - re; - ip[blocksize] = ip[0] - im; - rp[0] += re; - ip[0] += im; - - rp += blocksize * 2; - ip += blocksize * 2; - } - } -} - -// Reverse bits in a 32-bit number -static uint32_t rbit_32(uint32_t x) -{ -#if defined(__GNUC__) && defined(__ARM_ARCH_7M__) - __asm__("rbit %0,%0" : "=r"(x) : "0"(x)); - return x; -#else - x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); - x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); - x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); - x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); - return((x >> 16) | (x << 16)); -#endif -} - -// Reverse bits in an n-bit number. -static uint32_t rbit_n(uint32_t x, unsigned n) -{ - return rbit_32(x << (32 - n)); -} - -// Base-2 integer logarithm -static int ilog2(unsigned x) -{ - int result = -1; - while (x) - { - x >>= 1; - result++; - } - return result; -} - -// Compute a transform of the real-valued input array, and store results in two arrays. -// Size of each array is the same as transform_length. -// Transform length must be a power of two and atleast 4. -void fix16_fft(INPUT_TYPE *input, fix16_t *real, fix16_t *imag, unsigned transform_length) -{ - int log_length = ilog2(transform_length); - transform_length = 1 << log_length; - - unsigned i; - for (i = 0; i < transform_length / 4; i++) - { - four_point_dft(input + INPUT_INDEX(rbit_n(i, log_length - 2)), transform_length / 4, real + 4*i, imag + 4*i); - } - - for (i = 2; i < log_length; i++) - { - butterfly(real, imag, 1 << i, transform_length / (2 << i)); - } - -#ifdef OUTPUT_SCALE - fix16_t scale = OUTPUT_SCALE(transform_length); - for (i = 0; i < transform_length; i++) - { - real[i] = fix16_mul(real[i], scale); - imag[i] = fix16_mul(imag[i], scale); - } -#endif -} - -/* Just some test code -#include -int main() -{ - INPUT_TYPE input[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; - fix16_t real[16], imag[16]; - - fix16_fft(input, real, imag, 16); - - int count = 16; - int i; - for (i = 0; i < count; i++) - { - printf("%d: %0.4f, %0.4f\n", i, fix16_to_float(real[i]), fix16_to_float(imag[i])); - } - return 0; -} -*/ diff --git a/libfixmath/fix16.h b/libfixmath/fix16.h index eeb0b99..79c0090 100644 --- a/libfixmath/fix16.h +++ b/libfixmath/fix16.h @@ -38,6 +38,7 @@ static const fix16_t fix16_overflow = 0x80000000; /*!< the value used to indicat static const fix16_t fix16_pi = 205887; /*!< fix16_t value of pi */ static const fix16_t fix16_e = 178145; /*!< fix16_t value of e */ static const fix16_t fix16_one = 0x00010000; /*!< fix16_t value of 1 */ +static const fix16_t fix16_eps = 1; /*!< fix16_t epsilon */ /* Conversion functions between fix16_t and float/integer. * These are inlined to allow compiler to optimize away constant numbers diff --git a/libfixmath/fix16_fft.c b/libfixmath/fix16_fft.c new file mode 100644 index 0000000..1a5aeb3 --- /dev/null +++ b/libfixmath/fix16_fft.c @@ -0,0 +1,163 @@ +/* Real-input FFT implementation using the libfixmath fix16_t datatype. + * Not the fastest implementation ever, but has a small code size. + * + * Refer to http://www.dspguide.com/ch12/2.htm for information on the + * algorithm. + * + * (c) 2012 Petteri Aimonen + * This file is released to public domain. + */ + +#include +#include "fix16.h" + +// You can change the input datatype and intermediate scaling here. +// By default, the output is divided by the transform length to get a normalized FFT. +// Input_convert determines the scaling of intermediate values. Multiplication by 256 +// gives a nice compromise between precision and numeric range. +#ifndef INPUT_TYPE +#define INPUT_TYPE uint8_t +#endif + +#ifndef INPUT_CONVERT +#define INPUT_CONVERT(x) ((x) << 8) +#endif + +#ifndef INPUT_INDEX +#define INPUT_INDEX(x) (x) +#endif + +#ifndef OUTPUT_SCALE +#define OUTPUT_SCALE(transform_size) (fix16_one * 256 / transform_size) +#endif + +// Fast calculation of DFT for a 4-point signal. Based on the simplicity +// of 4-point sinewave. +static void four_point_dft(INPUT_TYPE *input, unsigned input_stride, + fix16_t *real, fix16_t *imag) +{ + fix16_t x0 = INPUT_CONVERT(input[0 * input_stride]); + fix16_t x1 = INPUT_CONVERT(input[1 * input_stride]); + fix16_t x2 = INPUT_CONVERT(input[2 * input_stride]); + fix16_t x3 = INPUT_CONVERT(input[3 * input_stride]); + + real[0] = x0 + x1 + x2 + x3; + imag[0] = 0; + real[1] = x0 - x2; + imag[1] = -x1 + x3; + real[2] = x0 - x1 + x2 - x3; + imag[2] = 0; + real[3] = x0 - x2; + imag[3] = x1 - x3; +} + +// Mix N blocksize-sized transforms pairwise together to get N/2 2*blocksize-sized transforms. +static void butterfly(fix16_t *real, fix16_t *imag, unsigned blocksize, unsigned blockpairs) +{ + unsigned i, j; + for (i = 0; i < blocksize; i++) + { + fix16_t angle = fix16_pi * i / blocksize; + fix16_t c = fix16_cos(angle); + fix16_t s = -fix16_sin(angle); + + fix16_t *rp = real + i; + fix16_t *ip = imag + i; + for (j = 0; j < blockpairs; j++) + { + // Get the odd-indexed tranform and multiply by sine + fix16_t re = fix16_mul(rp[blocksize], c) - fix16_mul(ip[blocksize], s); + fix16_t im = fix16_mul(ip[blocksize], c) + fix16_mul(rp[blocksize], s); + + // Update the transforms + rp[blocksize] = rp[0] - re; + ip[blocksize] = ip[0] - im; + rp[0] += re; + ip[0] += im; + + rp += blocksize * 2; + ip += blocksize * 2; + } + } +} + +// Reverse bits in a 32-bit number +static uint32_t rbit_32(uint32_t x) +{ +#if defined(__GNUC__) && defined(__ARM_ARCH_7M__) + __asm__("rbit %0,%0" : "=r"(x) : "0"(x)); + return x; +#else + x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); + x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); + x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); + x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); + return((x >> 16) | (x << 16)); +#endif +} + +// Reverse bits in an n-bit number. +static uint32_t rbit_n(uint32_t x, unsigned n) +{ + return rbit_32(x << (32 - n)); +} + +// Base-2 integer logarithm +static int ilog2(unsigned x) +{ + int result = -1; + while (x) + { + x >>= 1; + result++; + } + return result; +} + +// Compute a transform of the real-valued input array, and store results in two arrays. +// Size of each array is the same as transform_length. +// Transform length must be a power of two and atleast 4. +void fix16_fft(INPUT_TYPE *input, fix16_t *real, fix16_t *imag, unsigned transform_length) +{ + int log_length = ilog2(transform_length); + transform_length = 1 << log_length; + + unsigned i; + for (i = 0; i < transform_length / 4; i++) + { + four_point_dft(input + INPUT_INDEX(rbit_n(i, log_length - 2)), transform_length / 4, real + 4*i, imag + 4*i); + } + + for (i = 2; i < log_length; i++) + { + butterfly(real, imag, 1 << i, transform_length / (2 << i)); + } + +#ifdef OUTPUT_SCALE + fix16_t scale = OUTPUT_SCALE(transform_length); + for (i = 0; i < transform_length; i++) + { + real[i] = fix16_mul(real[i], scale); + imag[i] = fix16_mul(imag[i], scale); + } +#endif +} + +/* Just some test code +#include +int main() +{ + INPUT_TYPE input[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; + fix16_t real[16], imag[16]; + + fix16_fft(input, real, imag, 16); + + int count = 16; + int i; + for (i = 0; i < count; i++) + { + printf("%d: %0.4f, %0.4f\n", i, fix16_to_float(real[i]), fix16_to_float(imag[i])); + } + return 0; +} +*/ diff --git a/libfixmath/libfixmath.cmake b/libfixmath/libfixmath.cmake new file mode 100644 index 0000000..e4e7018 --- /dev/null +++ b/libfixmath/libfixmath.cmake @@ -0,0 +1,3 @@ +file(GLOB libfixmath-srcs libfixmath/*.h libfixmath/*.hpp libfixmath/*.c) + +add_library(libfixmath STATIC ${libfixmath-srcs}) diff --git a/tests/run_tests b/tests/run_tests new file mode 100755 index 0000000..bca1d68 --- /dev/null +++ b/tests/run_tests @@ -0,0 +1,2 @@ +#!/bin/env bash +for i in $(ls -1 tests_*); do ./$i; done diff --git a/tests/tests.c b/tests/tests.c new file mode 100644 index 0000000..cef9710 --- /dev/null +++ b/tests/tests.c @@ -0,0 +1,51 @@ +#include "tests.h" +#include "tests_basic.h" +#include "tests_lerp.h" +#include "tests_sqrt.h" +#include + +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 +}; + +unsigned stack_depth = 0; + +int main() +{ + printf("\033[1;34m\nVARIANT: \033[39m"STR2(PREFIX)"\033[0m\n"); + TEST(test_abs()); + TEST(test_add()); + TEST(test_mul()); + TEST(test_div()); + TEST(test_sub()); + TEST(test_sqrt()); + TEST(test_lerp()); + return 0; +} diff --git a/tests/tests.cmake b/tests/tests.cmake new file mode 100644 index 0000000..56aefe3 --- /dev/null +++ b/tests/tests.cmake @@ -0,0 +1,44 @@ +file(GLOB tests-srcs tests/*.c tests/*.h) + +set(ro64 PREFIX=ro64) +set(no64 PREFIX=no64 FIXMATH_NO_ROUNDING) +set(rn64 PREFIX=rn64 FIXMATH_NO_OVERFLOW) +set(nn64 PREFIX=nn64 FIXMATH_NO_ROUNDING FIXMATH_NO_OVERFLOW) +set(ro32 PREFIX=ro32 FIXMATH_NO_64BIT) +set(no32 PREFIX=no32 FIXMATH_NO_ROUNDING FIXMATH_NO_64BIT) +set(rn32 PREFIX=rn32 FIXMATH_NO_OVERFLOW FIXMATH_NO_64BIT) +set(nn32 PREFIX=nn32 FIXMATH_NO_OVERFLOW FIXMATH_NO_ROUNDING FIXMATH_NO_64BIT) +set(ro08 PREFIX=ro08 FIXMATH_OPTIMIZE_8BIT) +set(no08 PREFIX=no08 FIXMATH_NO_ROUNDING FIXMATH_OPTIMIZE_8BIT) +set(rn08 PREFIX=rn08 FIXMATH_NO_OVERFLOW FIXMATH_OPTIMIZE_8BIT) +set(nn08 PREFIX=nn08 FIXMATH_NO_OVERFLOW FIXMATH_NO_ROUNDING FIXMATH_OPTIMIZE_8BIT) + +configure_file(tests/run_tests ${CMAKE_BINARY_DIR}/run_tests COPYONLY) + +add_custom_target(tests) + +function(create_variant name defs) + add_library(libfixmath_${name} STATIC ${libfixmath-srcs}) + target_compile_definitions(libfixmath_${name} PRIVATE ${defs}) + 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}) + add_dependencies(tests tests_${name}) +endfunction() + + +create_variant("ro64" "${ro64}") +create_variant("no64" "${no64}") +create_variant("rn64" "${rn64}") +create_variant("nn64" "${nn64}") +create_variant("ro32" "${ro32}") +create_variant("no32" "${no32}") +create_variant("rn32" "${rn32}") +create_variant("nn32" "${nn32}") +create_variant("ro08" "${ro08}") +create_variant("no08" "${no08}") +create_variant("rn08" "${rn08}") +create_variant("nn08" "${nn08}") + + diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 0000000..6be58db --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,80 @@ +#ifndef TESTS_H +#define TESTS_H + +#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 TEST(x) \ + do \ + { \ + ++stack_depth; \ + if ((x)) \ + { \ + fflush(stdout); \ + fflush(stderr); \ + fprintf(stdout, \ + "\033[31;1m FAILED:\033[22;39m%*s" #x \ + " \033[0mat: " __FILE__ ":" STR2(__LINE__) " \n", \ + stack_depth, ""); \ + --stack_depth; \ + return 1; \ + } \ + else \ + { \ + fflush(stdout); \ + fflush(stderr); \ + printf("\033[32;1m OK:\033[22;39m%*s" #x "\n\033[0m", \ + stack_depth, ""); \ + } \ + --stack_depth; \ + } while (0) + +#define ASSERT_NEAR_DOUBLE(a, b, eps, ...) \ + do \ + { \ + if ((delta((a), (b)) >= (eps))) \ + { \ + fflush(stdout); \ + fflush(stderr); \ + fprintf(stdout, \ + "\033[31;1m FAILED:\033[22;39m%*sASSERT_NEAR a: %f, b: " \ + "%f, eps: %f\033[0m at: %s(), " __FILE__ \ + ":" STR2(__LINE__) "\n", \ + stack_depth, "", (a), (b), (eps), __func__); \ + fprintf(stdout, " %*s", stack_depth, ""); \ + fprintf(stdout, __VA_ARGS__); \ + return 1; \ + } \ + } while (0) + +#define ASSERT_EQ_INT(a, b) \ + do \ + { \ + if ((a) != (b)) \ + { \ + fflush(stdout); \ + fflush(stderr); \ + fprintf(stdout, \ + "\033[31;1m FAILED:\033[22;39m%*sASSERT_EQ a: %i, b: " \ + "%i\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])) + +#define delta(a, b) (((a) >= (b)) ? (a) - (b) : (b) - (a)) + +#endif // TESTS_H diff --git a/tests/tests_basic.c b/tests/tests_basic.c new file mode 100644 index 0000000..6037ae2 --- /dev/null +++ b/tests/tests_basic.c @@ -0,0 +1,246 @@ +#include "tests_basic.h" +#include "tests.h" + +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); + if (fa <= 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), "in: %f", fa); + } + } + return 0; +} + +int test_abs(void) +{ + TEST(test_abs_short()); + return 0; +} + +int test_add_short(void) +{ + for (unsigned i = 0; i < TESTCASES_COUNT; ++i) + { + for (unsigned 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); + double fresult = fa + fb; + + double max = fix16_to_dbl(fix16_maximum); + double min = fix16_to_dbl(fix16_minimum); + 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), "%f + %f", fa, fb); + } + } + } + return 0; +} + +int test_add(void) +{ + TEST(test_add_short()); + return 0; +} + +int test_mul_specific(void) +{ + ASSERT_EQ_INT(fix16_mul(fix16_from_int(5), fix16_from_int(5)), + fix16_from_int(25)); + ASSERT_EQ_INT(fix16_mul(fix16_from_int(-5), fix16_from_int(5)), + fix16_from_int(-25)); + ASSERT_EQ_INT(fix16_mul(fix16_from_int(-5), fix16_from_int(-5)), + fix16_from_int(25)); + ASSERT_EQ_INT(fix16_mul(fix16_from_int(5), fix16_from_int(-5)), + fix16_from_int(-25)); + + ASSERT_EQ_INT(fix16_mul(0, 10), 0); + ASSERT_EQ_INT(fix16_mul(2, 0x8000), 1); + ASSERT_EQ_INT(fix16_mul(-2, 0x8000), -1); +#ifndef FIXMATH_NO_ROUNDING + ASSERT_EQ_INT(fix16_mul(3, 0x8000), 2); + ASSERT_EQ_INT(fix16_mul(2, 0x7FFF), 1); + ASSERT_EQ_INT(fix16_mul(-2, 0x8001), -1); + ASSERT_EQ_INT(fix16_mul(-3, 0x8000), -2); + ASSERT_EQ_INT(fix16_mul(-2, 0x7FFF), -1); + ASSERT_EQ_INT(fix16_mul(2, 0x8001), 1); +#endif + return 0; +} + +int test_mul_short() +{ + for (unsigned i = 0; i < TESTCASES_COUNT; ++i) + { + for (unsigned 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); + double fresult = fa * fb; + + double max = fix16_to_dbl(fix16_maximum); + double min = fix16_to_dbl(fix16_minimum); + + 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), "%f * %f", fa, fb); + } + } + } + return 0; +} + +int test_mul(void) +{ + TEST(test_mul_specific()); + TEST(test_mul_short()); + return 0; +} + +int test_div_specific() +{ + ASSERT_EQ_INT(fix16_div(fix16_from_int(15), fix16_from_int(5)), + fix16_from_int(3)); + ASSERT_EQ_INT(fix16_div(fix16_from_int(-15), fix16_from_int(5)), + fix16_from_int(-3)); + ASSERT_EQ_INT(fix16_div(fix16_from_int(-15), fix16_from_int(-5)), + fix16_from_int(3)); + ASSERT_EQ_INT(fix16_div(fix16_from_int(15), fix16_from_int(-5)), + fix16_from_int(-3)); + +#ifndef FIXMATH_NO_ROUNDING + ASSERT_EQ_INT(fix16_div(0, 10), 0); + ASSERT_EQ_INT(fix16_div(1, fix16_from_int(2)), 1); + ASSERT_EQ_INT(fix16_div(-1, fix16_from_int(2)), -1); + ASSERT_EQ_INT(fix16_div(1, fix16_from_int(-2)), -1); + ASSERT_EQ_INT(fix16_div(-1, fix16_from_int(-2)), 1); + ASSERT_EQ_INT(fix16_div(3, fix16_from_int(2)), 2); + ASSERT_EQ_INT(fix16_div(-3, fix16_from_int(2)), -2); + ASSERT_EQ_INT(fix16_div(3, fix16_from_int(-2)), -2); + ASSERT_EQ_INT(fix16_div(-3, fix16_from_int(-2)), 2); + ASSERT_EQ_INT(fix16_div(2, 0x7FFF), 4); + ASSERT_EQ_INT(fix16_div(-2, 0x7FFF), -4); + ASSERT_EQ_INT(fix16_div(2, 0x8001), 4); + ASSERT_EQ_INT(fix16_div(-2, 0x8001), -4); +#endif + + return 0; +} + +int test_div_short() +{ + for (unsigned i = 0; i < TESTCASES_COUNT; ++i) + { + for (unsigned 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); + double fresult = fa / fb; + + double max = fix16_to_dbl(fix16_maximum); + double min = fix16_to_dbl(fix16_minimum); + + 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); + } + } + } + return 0; +} + +int test_div(void) +{ + TEST(test_div_specific()); + TEST(test_div_short()); + return 0; +} + +int test_sub_short() +{ + for (unsigned i = 0; i < TESTCASES_COUNT; ++i) + { + for (unsigned 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); + double fresult = fa - fb; + + double max = fix16_to_dbl(fix16_maximum); + double min = fix16_to_dbl(fix16_minimum); + 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), "%f - %f", fa, fb); + } + } + } + return 0; +} + +int test_sub() +{ + TEST(test_sub_short()); + return 0; +} diff --git a/tests/tests_basic.h b/tests/tests_basic.h new file mode 100644 index 0000000..a51f283 --- /dev/null +++ b/tests/tests_basic.h @@ -0,0 +1,10 @@ +#ifndef TESTS_BASIC_H +#define TESTS_BASIC_H + +int test_abs(void); +int test_add(void); +int test_mul(void); +int test_div(void); +int test_sub(); + +#endif // TESTS_BASIC_H diff --git a/tests/tests_lerp.c b/tests/tests_lerp.c new file mode 100644 index 0000000..b3a04f6 --- /dev/null +++ b/tests/tests_lerp.c @@ -0,0 +1,33 @@ +#include "tests_lerp.h" +#include "tests.h" + +int test_lerp() +{ + ASSERT_EQ_INT(fix16_lerp8(0, 2, 0), 0); + ASSERT_EQ_INT(fix16_lerp8(0, 2, 127), 0); + ASSERT_EQ_INT(fix16_lerp8(0, 2, 128), 1); + ASSERT_EQ_INT(fix16_lerp8(0, 2, 255), 1); + ASSERT_EQ_INT(fix16_lerp8(fix16_minimum, fix16_maximum, 0), fix16_minimum); + ASSERT_EQ_INT(fix16_lerp8(fix16_minimum, fix16_maximum, 255), + (fix16_maximum - (1 << 24))); + ASSERT_EQ_INT(fix16_lerp8(-fix16_maximum, fix16_maximum, 128), 0); + + ASSERT_EQ_INT(fix16_lerp16(0, 2, 0), 0); + ASSERT_EQ_INT(fix16_lerp16(0, 2, 0x7fff), 0); + ASSERT_EQ_INT(fix16_lerp16(0, 2, 0x8000), 1); + ASSERT_EQ_INT(fix16_lerp16(0, 2, 0xffff), 1); + ASSERT_EQ_INT(fix16_lerp16(fix16_minimum, fix16_maximum, 0), fix16_minimum); + ASSERT_EQ_INT(fix16_lerp16(fix16_minimum, fix16_maximum, 0xffff), + (fix16_maximum - (1 << 16))); + ASSERT_EQ_INT(fix16_lerp16(-fix16_maximum, fix16_maximum, 0x8000), 0); + + ASSERT_EQ_INT(fix16_lerp32(0, 2, 0), 0); + ASSERT_EQ_INT(fix16_lerp32(0, 2, 0x7fffffff), 0); + ASSERT_EQ_INT(fix16_lerp32(0, 2, 0x80000000), 1); + ASSERT_EQ_INT(fix16_lerp32(0, 2, 0xffffffff), 1); + ASSERT_EQ_INT(fix16_lerp32(fix16_minimum, fix16_maximum, 0), fix16_minimum); + ASSERT_EQ_INT(fix16_lerp32(fix16_minimum, fix16_maximum, 0xffffffff), + (fix16_maximum - 1)); + ASSERT_EQ_INT(fix16_lerp32(-fix16_maximum, fix16_maximum, 0x80000000), 0); + return 0; +} diff --git a/tests/tests_lerp.h b/tests/tests_lerp.h new file mode 100644 index 0000000..ab261dd --- /dev/null +++ b/tests/tests_lerp.h @@ -0,0 +1,6 @@ +#ifndef TESTS_LERP_H +#define TESTS_LERP_H + +int test_lerp(); + +#endif // TESTS_LERP_H diff --git a/tests/tests_sqrt.c b/tests/tests_sqrt.c new file mode 100644 index 0000000..8faf016 --- /dev/null +++ b/tests/tests_sqrt.c @@ -0,0 +1,37 @@ +#include "tests_sqrt.h" +#include "tests.h" + +int test_sqrt_specific() +{ + ASSERT_EQ_INT(fix16_sqrt(fix16_from_int(16)), fix16_from_int(4)); + ASSERT_EQ_INT(fix16_sqrt(fix16_from_int(100)), fix16_from_int(10)); + ASSERT_EQ_INT(fix16_sqrt(fix16_from_int(1)), fix16_from_int(1)); +#ifndef FIXMATH_NO_ROUNDING + ASSERT_EQ_INT(fix16_sqrt(214748302), 3751499); + ASSERT_EQ_INT(fix16_sqrt(214748303), 3751499); + ASSERT_EQ_INT(fix16_sqrt(214748359), 3751499); + ASSERT_EQ_INT(fix16_sqrt(214748360), 3751500); +#endif + return 0; +} + +int test_sqrt_short() +{ + for (unsigned i = 0; i < TESTCASES_COUNT; ++i) + { + fix16_t a = testcases[i]; + double fa = fix16_to_dbl(a); + fix16_t result = fix16_sqrt(a); + double fresult = sqrt(fa); + ASSERT_NEAR_DOUBLE(fresult, fix16_to_dbl(result), fix16_to_dbl(1), + "in: %f", fa); + } + return 0; +} + +int test_sqrt() +{ + TEST(test_sqrt_specific()); + TEST(test_sqrt_short()); + return 0; +} diff --git a/tests/tests_sqrt.h b/tests/tests_sqrt.h new file mode 100644 index 0000000..d0e108d --- /dev/null +++ b/tests/tests_sqrt.h @@ -0,0 +1,6 @@ +#ifndef TESTS_SQRT_H +#define TESTS_SQRT_H + +int test_sqrt(); + +#endif // TESTS_SQRT_H diff --git a/unittests/Makefile b/unittests/Makefile index 78433fe..612be42 100644 --- a/unittests/Makefile +++ b/unittests/Makefile @@ -2,7 +2,7 @@ CC = gcc # Basic CFLAGS for debugging -CFLAGS = -g -O0 -I../libfixmath -Wall -Wextra -Werror +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 \ diff --git a/unittests/fix16_exp_unittests.c b/unittests/fix16_exp_unittests.c index afb6706..9fc0617 100644 --- a/unittests/fix16_exp_unittests.c +++ b/unittests/fix16_exp_unittests.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include diff --git a/unittests/fix16_unittests.c b/unittests/fix16_unittests.c index 51d8f61..436c979 100644 --- a/unittests/fix16_unittests.c +++ b/unittests/fix16_unittests.c @@ -1,8 +1,8 @@ -#include +#include #include #include #include -#include "int64.h" +#include "libfixmath/int64.h" #include "unittests.h" const fix16_t testcases[] = { -- cgit v1.2.3