aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Baƙinka <marun1@email.cz>2021-05-03 12:43:54 +0200
committerGitHub <noreply@github.com>2021-05-03 13:43:54 +0300
commit0c88e55fa8f132e8ac15426911d155f136078302 (patch)
tree03fc1b22c0582bd57ae3a14095d3d9c9f949f0c9
parent4ea8f3ff12da61a5ceba690bf1e39d9385b42880 (diff)
downloadlibfixmath-0c88e55fa8f132e8ac15426911d155f136078302.tar.gz
fixed division, undefined behaviors and some improvements (#33)
* testing using ctest * emove old testing script * added github workflow CI * updated CI * added unit test for macros * F16() and F16C() are both rounding allways, so fix16_from_dbl should as well * added tests for strign operations, but these functions are in my opinion unreliable and tests are failing * removed old unittests * removed old unittests from cmake * problem with division using gcc * improved benchmark * clarification of problem with division * attempt to fix * fixed some undefined behaviors, fixed division
-rw-r--r--CMakeLists.txt5
-rw-r--r--benchmarks/Makefile4
-rw-r--r--benchmarks/benchmark.c45
-rw-r--r--benchmarks/interface-arm.c4
-rw-r--r--benchmarks/interface-avr.c5
-rw-r--r--benchmarks/interface.c12
-rw-r--r--benchmarks/interface.h11
-rw-r--r--benchmarks/results-avr.odsbin0 -> 18676 bytes
-rw-r--r--libfixmath/fix16.c37
-rw-r--r--libfixmath/fix16.h20
-rw-r--r--libfixmath/fix16_sqrt.c135
-rw-r--r--tests/tests.c38
-rw-r--r--tests/tests.cmake7
-rw-r--r--tests/tests.h33
-rw-r--r--tests/tests_basic.c10
-rw-r--r--tests/tests_macros.c100
-rw-r--r--tests/tests_macros.h6
-rw-r--r--tests/tests_str.c108
-rw-r--r--tests/tests_str.h6
-rw-r--r--unittests/Makefile78
-rw-r--r--unittests/fix16_exp_unittests.c124
-rw-r--r--unittests/fix16_macros_unittests.c110
-rw-r--r--unittests/fix16_str_unittests.c117
-rw-r--r--unittests/fix16_unittests.c391
-rw-r--r--unittests/run_unittests.sh16
-rw-r--r--unittests/unittests.h18
26 files changed, 445 insertions, 995 deletions
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 <stdio.h>
+#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 <stdint.h>
+/* 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
--- /dev/null
+++ b/benchmarks/results-avr.ods
Binary files 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 <stdio.h>
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 <math.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
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 <libfixmath/fix16.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdbool.h>
-#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 <libfixmath/fix16.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdbool.h>
-#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 <libfixmath/fix16.h>
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <stdbool.h>
-#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 <libfixmath/fix16.h>
-#include <stdio.h>
-#include <math.h>
-#include <stdbool.h>
-#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 <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"); \
- }
-
-