From cbbfe85a4adb27394898600eea62afbacdb962f3 Mon Sep 17 00:00:00 2001 From: PetteriAimonen Date: Tue, 30 Dec 2014 18:41:55 +0000 Subject: Add F16C() macro for defining values without float support. Implementation by Jonas Zeiger. --- libfixmath/fix16.h | 83 ++++++++++++++++++++++++++++ unittests/Makefile | 9 ++- unittests/fix16_macros_unittests.c | 110 +++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 unittests/fix16_macros_unittests.c diff --git a/libfixmath/fix16.h b/libfixmath/fix16.h index 6f3599b..35e101d 100644 --- a/libfixmath/fix16.h +++ b/libfixmath/fix16.h @@ -228,6 +228,89 @@ extern void fix16_to_str(fix16_t value, char *buf, int decimals); */ extern fix16_t fix16_from_str(const char *buf); +/** Helper macro for F16C. Replace token with its number of characters/digits. */ +#define FIXMATH_TOKLEN(token) ( sizeof( #token ) - 1 ) + +/** Helper macro for F16C. Handles pow(10, n) for n from 0 to 8. */ +#define FIXMATH_CONSTANT_POW10(times) ( \ + (times == 0) ? 1ULL \ + : (times == 1) ? 10ULL \ + : (times == 2) ? 100ULL \ + : (times == 3) ? 1000ULL \ + : (times == 4) ? 10000ULL \ + : (times == 5) ? 100000ULL \ + : (times == 6) ? 1000000ULL \ + : (times == 7) ? 10000000ULL \ + : 100000000ULL \ +) + + +/** Helper macro for F16C, the type uint64_t is only used at compile time and + * shouldn't be visible in the generated code. + * + * @note We do not use fix16_one instead of 65536ULL, because the + * "use of a const variable in a constant expression is nonstandard in C". + */ +#define FIXMATH_CONVERT_MANTISSA(m) \ +( (unsigned) \ + ( \ + ( \ + ( \ + (uint64_t)( ( ( 1 ## m ## ULL ) - FIXMATH_CONSTANT_POW10(FIXMATH_TOKLEN(m)) ) * FIXMATH_CONSTANT_POW10(5 - FIXMATH_TOKLEN(m)) ) \ + * 100000ULL * 65536ULL \ + ) \ + + 5000000000ULL /* rounding: + 0.5 */ \ + ) \ + / \ + 10000000000LL \ + ) \ +) + + +#define FIXMATH_COMBINE_I_M(i, m) \ +( \ + ( \ + ( i ) \ + << 16 \ + ) \ + | \ + ( \ + FIXMATH_CONVERT_MANTISSA(m) \ + & 0xFFFF \ + ) \ +) + + +/** Create int16_t (Q16.16) constant from separate integer and mantissa part. + * + * Only tested on 32-bit ARM Cortex-M0 / x86 Intel. + * + * This macro is needed when compiling with options like "--fpu=none", + * which forbid all and every use of float and related types and + * would thus make it impossible to have fix16_t constants. + * + * Just replace uses of F16() with F16C() like this: + * F16(123.1234) becomes F16C(123,1234) + * + * @warning Specification of any value outside the mentioned intervals + * WILL result in undefined behavior! + * + * @note Regardless of the specified minimum and maximum values for i and m below, + * the total value of the number represented by i and m MUST be in the interval + * ]-32768.00000:32767.99999[ else usage with this macro will yield undefined behavior. + * + * @param i Signed integer constant with a value in the interval ]-32768:32767[. + * @param m Positive integer constant in the interval ]0:99999[ (fractional part/mantissa). + */ +#define F16C(i, m) \ +( (fix16_t) \ + ( \ + (( #i[0] ) == '-') \ + ? -FIXMATH_COMBINE_I_M((unsigned)( ( (i) * -1) ), m) \ + : FIXMATH_COMBINE_I_M(i, m) \ + ) \ +) + #ifdef __cplusplus } #include "fix16.hpp" diff --git a/unittests/Makefile b/unittests/Makefile index 14dac10..329caf4 100644 --- a/unittests/Makefile +++ b/unittests/Makefile @@ -8,7 +8,7 @@ CFLAGS = -g -O0 -I../libfixmath -Wall -Wextra -Werror 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 +all: run_fix16_unittests run_fix16_exp_unittests run_fix16_str_unittests run_fix16_macros_unittests clean: rm -f fix16_unittests_???? @@ -63,3 +63,10 @@ run_fix16_str_unittests: fix16_str_unittests 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_macros_unittests.c b/unittests/fix16_macros_unittests.c new file mode 100644 index 0000000..cdfcfaf --- /dev/null +++ b/unittests/fix16_macros_unittests.c @@ -0,0 +1,110 @@ +/* This test checks that the F16() and F16C() macros work correctly. */ + +#include +#include +#include +#include +#include "unittests.h" + +#define DO_TEST(i,m) \ + TEST(F16(i ## . ## m) == F16C(i,m)) \ + TEST(F16(i ## . ## m) == fix16_from_dbl(i ## . ## m)) + +int main() +{ + int status = 0; + + /* Corner cases */ + DO_TEST(1,234) + DO_TEST(0,0) + DO_TEST(1,0) + DO_TEST(-1,0) + DO_TEST(1,5) + DO_TEST(-1,5) + DO_TEST(000000,00000) + DO_TEST(0,00001) + DO_TEST(0,00010) + DO_TEST(0,1) + DO_TEST(0,10001) + DO_TEST(0,11000) + DO_TEST(25,133) + DO_TEST(32767,00000) + DO_TEST(32767,00001) + DO_TEST(32767,99999) + DO_TEST(0,25) + DO_TEST(0,99555) + DO_TEST(0,99998) + DO_TEST(0,99999) + DO_TEST(-1,1) + DO_TEST(-25,133) + DO_TEST(-32767,00001) + DO_TEST(-32768,00000) + + /* Random values */ + DO_TEST( 0,02267) + DO_TEST( 1,49887) + DO_TEST( 0,27589) + DO_TEST( 0,38393) + DO_TEST( 0,08934) + DO_TEST( 0,95820) + DO_TEST( 0,95596) + DO_TEST( 72,10642) + DO_TEST( 0,48939) + DO_TEST( 3,37797) + DO_TEST( 1,09194) + DO_TEST( 0,08605) + DO_TEST( 3,04349) + DO_TEST( 3,95401) + DO_TEST( 15,36292) + DO_TEST( 56,09242) + DO_TEST( 0,54071) + DO_TEST( 27,08953) + DO_TEST( 0,03913) + DO_TEST( 1,32707) + DO_TEST( 4,50117) + DO_TEST( 0,24990) + DO_TEST( 44,77319) + DO_TEST( 2,59139) + DO_TEST( 0,16279) + DO_TEST( 17,14712) + DO_TEST( 11,54281) + DO_TEST( 0,02768) + DO_TEST( 0,39278) + DO_TEST( 0,19369) + DO_TEST( -0,04534) + DO_TEST( -0,00349) + DO_TEST( -2,30380) + DO_TEST( -0,03061) + DO_TEST( -7,50065) + DO_TEST( -3,97050) + DO_TEST( -0,43898) + DO_TEST( -3,49876) + DO_TEST( -1,35942) + DO_TEST( -10,81154) + DO_TEST( -0,26676) + DO_TEST( -9,52134) + DO_TEST( -0,42592) + DO_TEST( -0,05424) + DO_TEST( -0,62461) + DO_TEST( -0,21562) + DO_TEST( -0,22366) + DO_TEST( -0,09074) + DO_TEST( -1,29527) + DO_TEST( -4,98427) + DO_TEST( -0,10721) + DO_TEST( -11,39446) + DO_TEST(-451,53916) + DO_TEST( -0,04279) + DO_TEST( -3,36543) + DO_TEST( -0,01003) + DO_TEST( -12,08326) + DO_TEST( -1,07143) + DO_TEST( -1,07737) + DO_TEST( -0,22957) + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +} + -- cgit v1.2.3