From 18a9ad00de49ce1724000b74a339e7dd3165bdd1 Mon Sep 17 00:00:00 2001 From: Chris Hammond Date: Thu, 29 Oct 2020 13:20:27 -0400 Subject: [PATCH] Removed 64-bit restriction on linear interpolation functions. Added more test cases and fixed several bugs in the 64-bit arithmetic functions. Also added script to run all unit tests. --- libfixmath/fix16.c | 16 +++++------ libfixmath/fix16.h | 2 -- libfixmath/int64.h | 57 +++++++++++++++++++++++-------------- unittests/fix16_unittests.c | 28 ++++++++++++++++-- unittests/run_unittests.sh | 16 +++++++++++ 5 files changed, 84 insertions(+), 35 deletions(-) create mode 100644 unittests/run_unittests.sh diff --git a/libfixmath/fix16.c b/libfixmath/fix16.c index ed7773a..4b1ae2b 100644 --- a/libfixmath/fix16.c +++ b/libfixmath/fix16.c @@ -480,9 +480,6 @@ fix16_t fix16_mod(fix16_t x, fix16_t y) return x; } - -#ifndef FIXMATH_NO_64BIT - fix16_t fix16_lerp8(fix16_t inArg0, fix16_t inArg1, uint8_t inFract) { int64_t tempOut = int64_mul_i32_i32(inArg0, (((int32_t)1 << 8) - inFract)); @@ -501,10 +498,11 @@ fix16_t fix16_lerp16(fix16_t inArg0, fix16_t inArg1, uint16_t inFract) fix16_t fix16_lerp32(fix16_t inArg0, fix16_t inArg1, uint32_t inFract) { - int64_t tempOut; - tempOut = ((int64_t)inArg0 * (((int64_t)1<<32) - inFract)); - tempOut += ((int64_t)inArg1 * inFract); - tempOut >>= 32; - return (fix16_t)tempOut; + if(inFract == 0) + return inArg0; + int64_t inFract64 = int64_const(0, inFract); + int64_t subbed = int64_sub(int64_const(1,0), inFract64); + int64_t tempOut = int64_mul_i64_i32(subbed, inArg0); + tempOut = int64_add(tempOut, int64_mul_i64_i32(inFract64, inArg1)); + return int64_hi(tempOut); } -#endif diff --git a/libfixmath/fix16.h b/libfixmath/fix16.h index 644776b..c6be250 100644 --- a/libfixmath/fix16.h +++ b/libfixmath/fix16.h @@ -144,9 +144,7 @@ extern fix16_t fix16_mod(fix16_t x, fix16_t y) FIXMATH_FUNC_ATTRS; */ extern fix16_t fix16_lerp8(fix16_t inArg0, fix16_t inArg1, uint8_t inFract) FIXMATH_FUNC_ATTRS; extern fix16_t fix16_lerp16(fix16_t inArg0, fix16_t inArg1, uint16_t inFract) FIXMATH_FUNC_ATTRS; -#ifndef FIXMATH_NO_64BIT extern fix16_t fix16_lerp32(fix16_t inArg0, fix16_t inArg1, uint32_t inFract) FIXMATH_FUNC_ATTRS; -#endif diff --git a/libfixmath/int64.h b/libfixmath/int64.h index 82ec9f7..8a3d26a 100644 --- a/libfixmath/int64.h +++ b/libfixmath/int64.h @@ -53,7 +53,7 @@ static inline _int64_t int64_add(_int64_t x, _int64_t y) { _int64_t ret; ret.hi = x.hi + y.hi; ret.lo = x.lo + y.lo; - if((ret.lo < x.lo) || (ret.hi < y.hi)) + if((ret.lo < x.lo) || (ret.lo < y.lo)) ret.hi++; return ret; } @@ -72,18 +72,25 @@ static inline _int64_t int64_sub(_int64_t x, _int64_t y) { } static inline _int64_t int64_shift(_int64_t x, int8_t y) { - _int64_t ret; - if(y > 0) { - if(y >= 32) - return (_int64_t){ 0, 0 }; + _int64_t ret = {0,0}; + if(y >= 64 || y <= -64) + return (_int64_t){ 0, 0 }; + if(y >= 32) { + ret.hi = (x.lo << (y - 32)); + } + else if(y > 0) { ret.hi = (x.hi << y) | (x.lo >> (32 - y)); ret.lo = (x.lo << y); - } else { + } + else { y = -y; - if(y >= 32) - return (_int64_t){ 0, 0 }; - ret.lo = (x.lo >> y) | (x.hi << (32 - y)); - ret.hi = (x.hi >> y); + if(y >= 32){ + ret.lo = (x.hi >> (y - 32)); + ret.hi = (x.hi < 0) ? -1 : 0; + } else { + ret.lo = (x.lo >> y) | (x.hi << (32 - y)); + ret.hi = (x.hi >> y); + } } return ret; } @@ -96,31 +103,37 @@ static inline _int64_t int64_mul_i32_i32(int32_t x, int32_t y) { int32_t r_md = (hi[0] * lo[1]) + (hi[1] * lo[0]); uint32_t r_lo = lo[0] * lo[1]; - r_hi += (r_md >> 16); - r_lo += (r_md << 16); + _int64_t r_hilo64 = (_int64_t){ r_hi, r_lo }; + _int64_t r_md64 = int64_shift(int64_from_int32(r_md), 16); - return (_int64_t){ r_hi, r_lo }; + return int64_add(r_hilo64, r_md64); } static inline _int64_t int64_mul_i64_i32(_int64_t x, int32_t y) { int neg = ((x.hi ^ y) < 0); if(x.hi < 0) x = int64_neg(x); - if(y < 0) - y = -y; + uint32_t ypos = (y < 0)? (-y) : (y); - uint32_t _x[4] = { (x.hi >> 16), (x.hi & 0xFFFF), (x.lo >> 16), (x.lo & 0xFFFF) }; - uint32_t _y[2] = { (y >> 16), (y & 0xFFFF) }; + uint32_t _x[4] = { (x.lo & 0xFFFF), (x.lo >> 16), (x.hi & 0xFFFF), (x.hi >> 16) }; + uint32_t _y[2] = { (ypos & 0xFFFF), (ypos >> 16) }; uint32_t r[4]; r[0] = (_x[0] * _y[0]); - r[1] = (_x[1] * _y[0]) + (_x[0] * _y[1]); - r[2] = (_x[1] * _y[1]) + (_x[2] * _y[0]); - r[3] = (_x[2] * _y[0]) + (_x[1] * _y[1]); + r[1] = (_x[1] * _y[0]); + uint32_t temp_r1 = r[1]; + r[1] += (_x[0] * _y[1]); + r[2] = (_x[2] * _y[0]) + (_x[1] * _y[1]); + r[3] = (_x[3] * _y[0]) + (_x[2] * _y[1]); + // Detect carry bit in r[1]. r[0] can't carry, and r[2]/r[3] don't matter. + if(r[1] < temp_r1) + r[3] ++; + _int64_t middle = int64_shift(int64_const(0, r[1]), 16); _int64_t ret; - ret.lo = r[0] + (r[1] << 16); - ret.hi = (r[3] << 16) + r[2] + (r[1] >> 16); + ret.lo = r[0]; + ret.hi = (r[3] << 16) + r[2]; + ret = int64_add(ret, middle); return (neg ? int64_neg(ret) : ret); } diff --git a/unittests/fix16_unittests.c b/unittests/fix16_unittests.c index b63c25d..51d8f61 100644 --- a/unittests/fix16_unittests.c +++ b/unittests/fix16_unittests.c @@ -2,6 +2,7 @@ #include #include #include +#include "int64.h" #include "unittests.h" const fix16_t testcases[] = { @@ -330,7 +331,31 @@ int main() TEST(failures == 0); } -#ifndef FIXMATH_NO_64BIT + { + 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"); @@ -358,7 +383,6 @@ int main() TEST(fix16_lerp32(fix16_minimum, fix16_maximum, 0xffffffff) == (fix16_maximum - 1)); TEST(fix16_lerp32(-fix16_maximum, fix16_maximum, 0x80000000) == 0); } -#endif if (status != 0) fprintf(stdout, "\n\nSome tests FAILED!\n"); diff --git a/unittests/run_unittests.sh b/unittests/run_unittests.sh new file mode 100644 index 0000000..1fc1469 --- /dev/null +++ b/unittests/run_unittests.sh @@ -0,0 +1,16 @@ +#!/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