From: Damir Tomic Date: Mon, 8 Jun 2015 08:29:19 +0000 (+0200) Subject: added incomplete test for lfpfunc.c. Changes to makefile and created c_timestructs... X-Git-Tag: NTP_4_3_40~6^2~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=07729d048f9794e21491603508d4adca8eaa99a4;p=thirdparty%2Fntp.git added incomplete test for lfpfunc.c. Changes to makefile and created c_timestructs.c and .h, because timestructs.cpp is a big c++ wrapper. Slowly converting it all to C code bk: 5575525fANFmrmD8NRZAzpGl0iaf9Q --- diff --git a/tests/libntp/c_timestructs.c b/tests/libntp/c_timestructs.c new file mode 100644 index 000000000..889b8c00c --- /dev/null +++ b/tests/libntp/c_timestructs.c @@ -0,0 +1,155 @@ +/* + * timestructs.cpp -- test bed adaptors for time structs. + * + * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * The contents of 'html/copyright.html' apply. + */ +#include "libntptest.h" +#include "timestructs.h" + +#include "timetoa.h" +#include "timevalops.h" +#include "timespecops.h" + + +namespace timeStruct { + +std::ostream& +operator << (std::ostream& os, const timeStruct::l_fp_wrap& val) +{ + // raw data formatting + os << "0x" << std::hex << val.V.l_ui << ':' + << std::setfill('0') << std::setw(8) << val.V.l_uf + << std::dec; + // human-readable format + os << '[' << lfptoa(&val.V, 10) << ']'; + return os; +} + +std::ostream& +operator << (std::ostream& os, const timeStruct::timeval_wrap& val) +{ + // raw data formatting + os << val.V.tv_sec << ':' << val.V.tv_usec; + // human-readable format + os << '[' + << format_time_fraction(val.V.tv_sec, val.V.tv_usec, 6) + << ']'; + return os; +} + +std::ostream& +operator << (std::ostream& os, const timeStruct::timespec_wrap& val) +{ + // raw data formatting + os << val.V.tv_sec << ':' << val.V.tv_nsec; + // human-readable format + os << '[' + << format_time_fraction(val.V.tv_sec, val.V.tv_nsec, 9) + << ']'; + return os; +} + +// Implementation of the l_fp closeness predicate + +AssertFpClose::AssertFpClose( + u_int32 hi, + u_int32 lo + ) +{ + limit.l_ui = hi; + limit.l_uf = lo; +} + +::testing::AssertionResult +AssertFpClose::operator()( + const char* m_expr, + const char* n_expr, + const l_fp & m, + const l_fp & n + ) +{ + l_fp diff; + + if (L_ISGEQ(&m, &n)) { + diff = m; + L_SUB(&diff, &n); + } else { + diff = n; + L_SUB(&diff, &m); + } + if (L_ISGEQ(&limit, &diff)) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << m_expr << " which is " << l_fp_wrap(m) + << "\nand\n" + << n_expr << " which is " << l_fp_wrap(n) + << "\nare not close; diff=" << l_fp_wrap(diff); +} + +// Implementation of the timeval closeness predicate + +AssertTimevalClose::AssertTimevalClose( + time_t hi, + int32 lo + ) +{ + limit.tv_sec = hi; + limit.tv_usec = lo; +} + +::testing::AssertionResult +AssertTimevalClose::operator()( + const char* m_expr, + const char* n_expr, + const struct timeval & m, + const struct timeval & n + ) +{ + struct timeval diff; + + diff = abs_tval(sub_tval(m, n)); + if (cmp_tval(limit, diff) >= 0) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << m_expr << " which is " << timeval_wrap(m) + << "\nand\n" + << n_expr << " which is " << timeval_wrap(n) + << "\nare not close; diff=" << timeval_wrap(diff); +} + +// Implementation of the timespec closeness predicate + +AssertTimespecClose::AssertTimespecClose( + time_t hi, + int32 lo + ) +{ + limit.tv_sec = hi; + limit.tv_nsec = lo; +} + +::testing::AssertionResult +AssertTimespecClose::operator()( + const char* m_expr, + const char* n_expr, + const struct timespec & m, + const struct timespec & n + ) +{ + struct timespec diff; + + diff = abs_tspec(sub_tspec(m, n)); + if (cmp_tspec(limit, diff) >= 0) + return ::testing::AssertionSuccess(); + + return ::testing::AssertionFailure() + << m_expr << " which is " << timespec_wrap(m) + << "\nand\n" + << n_expr << " which is " << timespec_wrap(n) + << "\nare not close; diff=" << timespec_wrap(diff); +} + +} // namespace timeStruct diff --git a/tests/libntp/c_timestructs.h b/tests/libntp/c_timestructs.h new file mode 100644 index 000000000..8cd59270d --- /dev/null +++ b/tests/libntp/c_timestructs.h @@ -0,0 +1,213 @@ +/* + * timestructs.h -- test bed adaptors for time structs. + * + * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. + * The contents of 'html/copyright.html' apply. + * + * Some wrapper classes and a closeness predicate that are used to + * bridge the gap between the goggletest framework and the structs used + * for representing time stamps (l_fp, struct timeval, struct timespec). + * + * Some ostream conversion operators are provided to give diagnostic + * output on errors. The normal string conversion functions will give + * HRVs (human readable values) but we might also be interested in the + * machine representation for diagnostic purposes. + */ + + +//#ifndef TESTCALSHIMS_H +#include "testcalshims.h" +//#endif + + +#ifndef TIMESTRUCTS_H +#define TIMESTRUCTS_H + +#include "ntp_fp.h" + + +l_fp test ; +//namespace timeStruct { + +// wrap a l_fp struct with common operations +struct l_fp_wrap { + + l_fp V; + + + //bool operator == (const l_fp_wrap& rhs) const + // { return L_ISEQU(&V, &rhs.V); } + +//---------------THIS HAS TO BE MANUALLY CONVERTED IN CODE!!! + //operator l_fp* () + // { return &V; } + //operator l_fp& () + // { return V; } +//----------------------------------------- +/* + l_fp_wrap & operator = (const l_fp_wrap& rhs) + { V = rhs.V; return *this; } + l_fp_wrap& operator = (const l_fp& rhs) + { V = rhs; return *this; } +*/ +}; + +l_fp_wrap(l_fp V){ + ZERO(V); } +l_fp_wrap(l_fp V, u_int32 hi, u_int32 lo){ + V.l_ui = hi; V.l_uf = lo; } +l_fp_wrap(l_fp V, const l_fp &rhs){ + V = rhs; } + +l_fp_wrap_equals(const l_fp_wrap& current, const l_fp_wrap& rhs) const // operator == + { return L_ISEQU(¤t.V, &rhs.V); } + +l_fp_wrap_and(const l_fp_wrap& current, const l_fp_wrap& rhs){ + V = rhs.V; + return current//*this; +} +l_fp_wrap_and(const l_fp& current, const l_fp& rhs){ + V = rhs; + return current//*this; +} + + + +// wrap a 'struct timeval' with common operations +struct timeval_wrap { + + struct timeval V; + + timeval_wrap() + { ZERO(V); } + timeval_wrap(time_t hi, long lo) + { V.tv_sec = hi; V.tv_usec = lo; } + timeval_wrap(const struct timeval & rhs) + { V = rhs; } + timeval_wrap(const timeval_wrap & rhs) + { V = rhs.V; } + bool operator == (const timeval_wrap& rhs) const + { return V.tv_sec == rhs.V.tv_sec && + V.tv_usec == rhs.V.tv_usec ; } + bool valid() const + { return V.tv_usec >= 0 && V.tv_usec < 1000000; } + operator struct timeval* () + { return &V; } + operator struct timeval& () + { return V; } + timeval_wrap& operator = (const timeval_wrap& rhs) + { V = rhs.V; return *this; } + timeval_wrap& operator = (const struct timeval& rhs) + { V = rhs; return *this; } +}; + +// wrap a 'struct timespec' with common operations +struct timespec_wrap { + + struct timespec V; + + timespec_wrap() + { ZERO(V); } + timespec_wrap(time_t hi, long lo) + { V.tv_sec = hi; V.tv_nsec = lo; } + timespec_wrap(const struct timespec & rhs) + { V = rhs; } + timespec_wrap(const timespec_wrap & rhs) + { V = rhs.V; } + bool operator == (const timespec_wrap& rhs) const + { return V.tv_sec == rhs.V.tv_sec && + V.tv_nsec == rhs.V.tv_nsec ; } + bool valid() const + { return V.tv_nsec >= 0 && V.tv_nsec < 1000000000; } + operator struct timespec* () + { return &V; } + operator struct timespec& () + { return V; } + timespec_wrap& operator = (const timespec_wrap& rhs) + { V = rhs.V; return *this; } + timespec_wrap& operator = (const struct timespec& rhs) + { V = rhs; return *this; } +}; + +// l_fp closeness testing predicate +// +// This predicate is used for the closeness ('near') testing of l_fp +// values. Once constructed with a limit, it can be used to check the +// absolute difference of two l_fp structs against that limit; if the +// difference is less or equal to this limit, the test passes. +class AssertFpClose { +private: + l_fp limit; + +public: + AssertFpClose(u_int32 hi, u_int32 lo); + + ::testing::AssertionResult + operator()(const char* m_expr, const char* n_expr, + const l_fp & m, const l_fp & n); +}; + + +// timeval closeness testing predicate +// +// CAVEAT: This class uses the timevalops functions +// - sub_tval +// - abs_tval +// - cmp_tval +// +// This creates a dependency loop of sorts. The loop is defused by the +// fact that these basic operations can be tested by exact value tests, +// so once the basic timeval operations passed it's safe to use this +// predicate. +class AssertTimevalClose { +private: + struct timeval limit; + +public: + // note: (hi,lo) should be a positive normalised timeval; + // the constructor does not normalise the values! + AssertTimevalClose(time_t hi, int32 lo); + + ::testing::AssertionResult + operator()(const char* m_expr, const char* n_expr, + const struct timeval & m, const struct timeval & n); +}; + + +// timespec closeness testing predicate +// +// CAVEAT: This class uses the timespecops functions +// - sub_tspec +// - abs_tspec +// - cmp_tspec +// +// See the equivalent timeval helper. +class AssertTimespecClose { +private: + struct timespec limit; + +public: + // note: (hi,lo) should be a positive normalised timespec; + // the constructor does not normalise the values! + AssertTimespecClose(time_t hi, int32 lo); + + ::testing::AssertionResult + operator()(const char* m_expr, const char* n_expr, + const struct timespec & m, const struct timespec & n); +}; + + +// since googletest wants to string format items, we declare the +// necessary operators. Since all adaptors have only public members +// there is need for friend declarations anywhere. + +extern std::ostream& operator << (std::ostream& os, + const timeStruct::l_fp_wrap& val); +extern std::ostream& operator << (std::ostream& os, + const timeStruct::timeval_wrap& val); +extern std::ostream& operator << (std::ostream& os, + const timeStruct::timespec_wrap& val); + +//} // namespace timeStruct + +#endif // TIMESTRUCTS_H diff --git a/tests/libntp/lfpfunc.cpp b/tests/libntp/g_lfpfunc.cpp similarity index 100% rename from tests/libntp/lfpfunc.cpp rename to tests/libntp/g_lfpfunc.cpp diff --git a/tests/libntp/lfpfunc.c b/tests/libntp/lfpfunc.c new file mode 100644 index 000000000..573e53ece --- /dev/null +++ b/tests/libntp/lfpfunc.c @@ -0,0 +1,643 @@ +//#include "c_timestructs.h" + +#include "testcalshims.h" +#include "unity.h" + +//#include "libntptest.h" +//#include "timestructs.h" + + + +#include "ntp_fp.h" + + +#include +#include + +#include // +//#include + + +typedef struct { + uint32_t h, l; +} lfp_hl; + + +/* +//---------------------------------------------------------------------- +// OO-wrapper for 'l_fp' +//---------------------------------------------------------------------- + +class LFP +{ +public: + ~LFP(); + LFP(); + LFP(const LFP& rhs); + LFP(int32 i, u_int32 f); + + LFP operator+ (const LFP &rhs) const; + LFP& operator+=(const LFP &rhs); + + LFP operator- (const LFP &rhs) const; + LFP& operator-=(const LFP &rhs); + + LFP& operator=(const LFP &rhs); + LFP operator-() const; + + bool operator==(const LFP &rhs) const; + + LFP neg() const; + LFP abs() const; + int signum() const; + + bool l_isgt (const LFP &rhs) const + { return L_ISGT(&_v, &rhs._v); } + bool l_isgtu(const LFP &rhs) const + { return L_ISGTU(&_v, &rhs._v); } + bool l_ishis(const LFP &rhs) const + { return L_ISHIS(&_v, &rhs._v); } + bool l_isgeq(const LFP &rhs) const + { return L_ISGEQ(&_v, &rhs._v); } + bool l_isequ(const LFP &rhs) const + { return L_ISEQU(&_v, &rhs._v); } + + int ucmp(const LFP & rhs) const; + int scmp(const LFP & rhs) const; + + std::string toString() const; + std::ostream& toStream(std::ostream &oo) const; + + operator double() const; + explicit LFP(double); + +protected: + LFP(const l_fp &rhs); + + static int cmp_work(u_int32 a[3], u_int32 b[3]); + + l_fp _v; +}; + +static std::ostream& operator<<(std::ostream &oo, const LFP& rhs) +{ + return rhs.toStream(oo); +} + +//---------------------------------------------------------------------- +// reference comparision +// This is implementad as a full signed MP-subtract in 3 limbs, where +// the operands are zero or sign extended before the subtraction is +// executed. +//---------------------------------------------------------------------- +int LFP::scmp(const LFP & rhs) const +{ + u_int32 a[3], b[3]; + const l_fp &op1(_v), &op2(rhs._v); + + a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0; + b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0; + + a[2] -= (op1.l_i < 0); + b[2] -= (op2.l_i < 0); + + return cmp_work(a,b); +} + +int LFP::ucmp(const LFP & rhs) const +{ + u_int32 a[3], b[3]; + const l_fp &op1(_v), &op2(rhs._v); + + a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0; + b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0; + + return cmp_work(a,b); +} + +int LFP::cmp_work(u_int32 a[3], u_int32 b[3]) +{ + u_int32 cy, idx, tmp; + for (cy = idx = 0; idx < 3; ++idx) { + tmp = a[idx]; cy = (a[idx] -= cy ) > tmp; + tmp = a[idx]; cy |= (a[idx] -= b[idx]) > tmp; + } + if (a[2]) + return -1; + return a[0] || a[1]; +} + +//---------------------------------------------------------------------- +// imlementation of the LFP stuff +// This should be easy enough... +//---------------------------------------------------------------------- + +LFP::~LFP() +{ + // NOP +} + +LFP::LFP() +{ + _v.l_ui = 0; + _v.l_uf = 0; +} + +LFP::LFP(int32 i, u_int32 f) +{ + _v.l_i = i; + _v.l_uf = f; +} + +LFP::LFP(const LFP &rhs) +{ + _v = rhs._v; +} + +LFP::LFP(const l_fp & rhs) +{ + _v = rhs; +} + +LFP& LFP::operator=(const LFP & rhs) +{ + _v = rhs._v; + return *this; +} + +LFP& LFP::operator+=(const LFP & rhs) +{ + L_ADD(&_v, &rhs._v); + return *this; +} + +LFP& LFP::operator-=(const LFP & rhs) +{ + L_SUB(&_v, &rhs._v); + return *this; +} + +LFP LFP::operator+(const LFP &rhs) const +{ + LFP tmp(*this); + return tmp += rhs; +} + +LFP LFP::operator-(const LFP &rhs) const +{ + LFP tmp(*this); + return tmp -= rhs; +} + +LFP LFP::operator-() const +{ + LFP tmp(*this); + L_NEG(&tmp._v); + return tmp; +} + +LFP +LFP::neg() const +{ + LFP tmp(*this); + L_NEG(&tmp._v); + return tmp; +} + +LFP +LFP::abs() const +{ + LFP tmp(*this); + if (L_ISNEG(&tmp._v)) + L_NEG(&tmp._v); + return tmp; +} + +int +LFP::signum() const +{ + if (_v.l_ui & 0x80000000u) + return -1; + return (_v.l_ui || _v.l_uf); +} + +std::string +LFP::toString() const +{ + std::ostringstream oss; + toStream(oss); + return oss.str(); +} + +std::ostream& +LFP::toStream(std::ostream &os) const +{ + return os + << mfptoa(_v.l_ui, _v.l_uf, 9) + << " [$" << std::setw(8) << std::setfill('0') << std::hex << _v.l_ui + << ':' << std::setw(8) << std::setfill('0') << std::hex << _v.l_uf + << ']'; +} + +bool LFP::operator==(const LFP &rhs) const +{ + return L_ISEQU(&_v, &rhs._v); +} + + +LFP::operator double() const +{ + double res; + LFPTOD(&_v, res); + return res; +} + +LFP::LFP(double rhs) +{ + DTOLFP(rhs, &_v); +} + + +//---------------------------------------------------------------------- +// testing the relational macros works better with proper predicate +// formatting functions; it slows down the tests a bit, but makes for +// readable failure messages. +//---------------------------------------------------------------------- + +testing::AssertionResult isgt_p( + const LFP &op1, const LFP &op2) +{ + if (op1.l_isgt(op2)) + return testing::AssertionSuccess() + << "L_ISGT(" << op1 << "," << op2 << ") is true"; + else + return testing::AssertionFailure() + << "L_ISGT(" << op1 << "," << op2 << ") is false"; +} + +testing::AssertionResult isgeq_p( + const LFP &op1, const LFP &op2) +{ + if (op1.l_isgeq(op2)) + return testing::AssertionSuccess() + << "L_ISGEQ(" << op1 << "," << op2 << ") is true"; + else + return testing::AssertionFailure() + << "L_ISGEQ(" << op1 << "," << op2 << ") is false"; +} + +testing::AssertionResult isgtu_p( + const LFP &op1, const LFP &op2) +{ + if (op1.l_isgtu(op2)) + return testing::AssertionSuccess() + << "L_ISGTU(" << op1 << "," << op2 << ") is true"; + else + return testing::AssertionFailure() + << "L_ISGTU(" << op1 << "," << op2 << ") is false"; +} + +testing::AssertionResult ishis_p( + const LFP &op1, const LFP &op2) +{ + if (op1.l_ishis(op2)) + return testing::AssertionSuccess() + << "L_ISHIS(" << op1 << "," << op2 << ") is true"; + else + return testing::AssertionFailure() + << "L_ISHIS(" << op1 << "," << op2 << ") is false"; +} + +testing::AssertionResult isequ_p( + const LFP &op1, const LFP &op2) +{ + if (op1.l_isequ(op2)) + return testing::AssertionSuccess() + << "L_ISEQU(" << op1 << "," << op2 << ") is true"; + else + return testing::AssertionFailure() + << "L_ISEQU(" << op1 << "," << op2 << ") is false"; +} + +*/ + +//---------------------------------------------------------------------- +// test data table for add/sub and compare +//---------------------------------------------------------------------- + + +static const lfp_hl addsub_tab[][3] = { + // trivial idendity: + {{0 ,0 }, { 0,0 }, { 0,0}}, + // with carry from fraction and sign change: + {{-1,0x80000000}, { 0,0x80000000}, { 0,0}}, + // without carry from fraction + {{ 1,0x40000000}, { 1,0x40000000}, { 2,0x80000000}}, + // with carry from fraction: + {{ 1,0xC0000000}, { 1,0xC0000000}, { 3,0x80000000}}, + // with carry from fraction and sign change: + {{0x7FFFFFFF, 0x7FFFFFFF}, {0x7FFFFFFF,0x7FFFFFFF}, {0xFFFFFFFE,0xFFFFFFFE}}, + // two tests w/o carry (used for l_fp<-->double): + {{0x55555555,0xAAAAAAAA}, {0x11111111,0x11111111}, {0x66666666,0xBBBBBBBB}}, + {{0x55555555,0x55555555}, {0x11111111,0x11111111}, {0x66666666,0x66666666}}, + // wide-range test, triggers compare trouble + {{0x80000000,0x00000001}, {0xFFFFFFFF,0xFFFFFFFE}, {0x7FFFFFFF,0xFFFFFFFF}} +}; +static const size_t addsub_cnt = (sizeof(addsub_tab)/sizeof(addsub_tab[0])); +static const size_t addsub_tot = (sizeof(addsub_tab)/sizeof(addsub_tab[0][0])); + + + +//---------------------------------------------------------------------- +// epsilon estimation for the precision of a conversion double --> l_fp +// +// The error estimation limit is as follows: +// * The 'l_fp' fixed point fraction has 32 bits precision, so we allow +// for the LSB to toggle by clamping the epsilon to be at least 2^(-31) +// +// * The double mantissa has a precsion 54 bits, so the other minimum is +// dval * (2^(-53)) +// +// The maximum of those two boundaries is used for the check. +// +// Note: once there are more than 54 bits between the highest and lowest +// '1'-bit of the l_fp value, the roundtrip *will* create truncation +// errors. This is an inherent property caused by the 54-bit mantissa of +// the 'double' type. +double eps(double d) +{ + return fmax(ldexp(1.0, -31), ldexp(fabs(d), -53)); //max +} + +//Y U NO OVERLOAD, C XD +/* +l_fp l_fp_init() +{ + l_fp temp; + temp.l_ui = 0; + temp.l_uf = 0; + return temp; +} +*/ + +l_fp l_fp_init(int32 i, u_int32 f) +{ + l_fp temp; + temp.l_i = i; + temp.l_uf = f; + + return temp; +} + + + +l_fp l_fp_add(const l_fp first, const l_fp second) //&rhs!!! +{ + l_fp temp; + //LFP tmp(*this); + //return tmp += rhs; + temp = first; + L_ADD(&temp, &second); + //return first + second; + return temp; +} + +l_fp l_fp_subtract(const l_fp first, const l_fp second) //&rhs!!! +{ + l_fp temp; + //LFP tmp(*this); + //return tmp += rhs; + temp = first; + L_SUB(&temp, &second); + + return temp; +} + +l_fp l_fp_negate(const l_fp first) +{ + l_fp temp; + //LFP tmp(*this); + //return tmp += rhs; + temp = first; + L_NEG(&temp); + + return temp; +} +/* +//negation! +LFP LFP::operator-() const +{ + LFP tmp(*this); + L_NEG(&tmp._v); + return tmp; +} +*/ +//---------------------------------------------------------------------- +// test addition +//---------------------------------------------------------------------- +void test_AdditionLR() { + + size_t idx=0; + for (idx=0; idx < addsub_cnt; ++idx) { + + + l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + //LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l); + //LFP exp(addsub_tab[idx][2].h, addsub_tab[idx][2].l); + l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l); + //LFP res(op1 + op2); + l_fp res = l_fp_add(op1,op2); + + TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp)); + } +} + +void test_AdditionRL() { + + size_t idx=0; + for (idx=0; idx < addsub_cnt; ++idx) { + l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + l_fp op1 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l); + l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l); + l_fp res = l_fp_add(op1,op2); + + TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp)); + } +} + + + +//---------------------------------------------------------------------- +// test subtraction +//---------------------------------------------------------------------- +void test_SubtractionLR() { + + size_t idx=0; + for (idx=0; idx < addsub_cnt; ++idx) { + l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + l_fp exp = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l); + l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l); + l_fp res = l_fp_subtract(op1,op2); + //LFP res(op1 - op2); + + TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp)); + } +} + +void test_SubtractionRL() { + + size_t idx=0; + for (idx=0; idx < addsub_cnt; ++idx) { + l_fp exp = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l); + l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l); + l_fp res = l_fp_subtract(op1,op2); + + TEST_ASSERT_EQUAL_MEMORY(&exp, &res,sizeof(exp)); + } +} + +//---------------------------------------------------------------------- +// test negation +//---------------------------------------------------------------------- + +void test_Negation() { + + size_t idx=0; + for (idx=0; idx < addsub_cnt; ++idx) { + l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + l_fp op2 = l_fp_negate(op1); + l_fp sum = l_fp_add(op1, op2); + + l_fp zero = l_fp_init(0,0); + TEST_ASSERT_EQUAL_MEMORY(&zero, &sum,sizeof(sum)); + + } +} + +/* + +//---------------------------------------------------------------------- +// test absolute value +//---------------------------------------------------------------------- +void test_Absolute() { + for (size_t idx=0; idx < addsub_cnt; ++idx) { + LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + LFP op2(op1.abs()); + + TEST_ASSERT_TRUE(op2.signum() >= 0); + + if (op1.signum() >= 0) + op1 -= op2; + else + op1 += op2; + TEST_ASSERT_EQUAL(LFP(0,0), op1); + } + + // There is one special case we have to check: the minimum + // value cannot be negated, or, to be more precise, the + // negation reproduces the original pattern. + LFP minVal(0x80000000, 0x00000000); + LFP minAbs(minVal.abs()); + TEST_ASSERT_EQUAL(-1, minVal.signum()); + TEST_ASSERT_EQUAL(minVal, minAbs); +} + +//---------------------------------------------------------------------- +// fp -> double -> fp rountrip test +//---------------------------------------------------------------------- +void test_FDF_RoundTrip() { + // since a l_fp has 64 bits in it's mantissa and a double has + // only 54 bits available (including the hidden '1') we have to + // make a few concessions on the roundtrip precision. The 'eps()' + // function makes an educated guess about the avilable precision + // and checks the difference in the two 'l_fp' values against + // that limit. + for (size_t idx=0; idx < addsub_cnt; ++idx) { + LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l); + double op2(op1); + LFP op3(op2); + // for manual checks only: + // std::cout << std::setprecision(16) << op2 << std::endl; + ASSERT_LE(fabs(op1-op3), eps(op2)); + } +} + +//---------------------------------------------------------------------- +// test the compare stuff +// +// This uses the local compare and checks if the operations using the +// macros in 'ntp_fp.h' produce mathing results. +// ---------------------------------------------------------------------- +void test_SignedRelOps() { + const lfp_hl * tv(&addsub_tab[0][0]); + for (size_t lc=addsub_tot-1; lc; --lc,++tv) { + LFP op1(tv[0].h,tv[0].l); + LFP op2(tv[1].h,tv[1].l); + int cmp(op1.scmp(op2)); + + switch (cmp) { + case -1: + std::swap(op1, op2); + case 1: + TEST_ASSERT_TRUE (isgt_p(op1,op2)); + TEST_ASSERT_FALSE(isgt_p(op2,op1)); + + TEST_ASSERT_TRUE (isgeq_p(op1,op2)); + TEST_ASSERT_FALSE(isgeq_p(op2,op1)); + + TEST_ASSERT_FALSE(isequ_p(op1,op2)); + TEST_ASSERT_FALSE(isequ_p(op2,op1)); + break; + case 0: + TEST_ASSERT_FALSE(isgt_p(op1,op2)); + TEST_ASSERT_FALSE(isgt_p(op2,op1)); + + TEST_ASSERT_TRUE (isgeq_p(op1,op2)); + TEST_ASSERT_TRUE (isgeq_p(op2,op1)); + + TEST_ASSERT_TRUE (isequ_p(op1,op2)); + TEST_ASSERT_TRUE (isequ_p(op2,op1)); + break; + default: + FAIL() << "unexpected SCMP result: " << cmp; + } + } +} + +void test_UnsignedRelOps() { + const lfp_hl * tv(&addsub_tab[0][0]); + for (size_t lc=addsub_tot-1; lc; --lc,++tv) { + LFP op1(tv[0].h,tv[0].l); + LFP op2(tv[1].h,tv[1].l); + int cmp(op1.ucmp(op2)); + + switch (cmp) { + case -1: + std::swap(op1, op2); + case 1: + TEST_ASSERT_TRUE (isgtu_p(op1,op2)); + TEST_ASSERT_FALSE(isgtu_p(op2,op1)); + + TEST_ASSERT_TRUE (ishis_p(op1,op2)); + TEST_ASSERT_FALSE(ishis_p(op2,op1)); + break; + case 0: + TEST_ASSERT_FALSE(isgtu_p(op1,op2)); + TEST_ASSERT_FALSE(isgtu_p(op2,op1)); + + TEST_ASSERT_TRUE (ishis_p(op1,op2)); + TEST_ASSERT_TRUE (ishis_p(op2,op1)); + break; + default: + FAIL() << "unexpected UCMP result: " << cmp; + } + } +} + +*/ + +//---------------------------------------------------------------------- +// that's all folks... but feel free to add things! +//---------------------------------------------------------------------- diff --git a/tests/libntp/run-test-lfpfunc.c b/tests/libntp/run-test-lfpfunc.c new file mode 100644 index 000000000..d4a78f740 --- /dev/null +++ b/tests/libntp/run-test-lfpfunc.c @@ -0,0 +1,56 @@ +/* AUTOGENERATED FILE. DO NOT EDIT. */ + +//=======Test Runner Used To Run Each Test Below===== +#define RUN_TEST(TestFunc, TestLineNum) \ +{ \ + Unity.CurrentTestName = #TestFunc; \ + Unity.CurrentTestLineNumber = TestLineNum; \ + Unity.NumberOfTests++; \ + if (TEST_PROTECT()) \ + { \ + setUp(); \ + TestFunc(); \ + } \ + if (TEST_PROTECT() && !TEST_IS_IGNORED) \ + { \ + tearDown(); \ + } \ + UnityConcludeTest(); \ +} + +//=======Automagically Detected Files To Include===== +#include "unity.h" +#include +#include + +//=======External Functions This Runner Calls===== +extern void setUp(void); +extern void tearDown(void); +extern void test_AdditionLR(); +extern void test_AdditionRL(); +extern void test_SubtractionLR(); +extern void test_SubtractionRL(); +extern void test_Negation(); + + +//=======Test Reset Option===== +void resetTest() +{ + tearDown(); + setUp(); +} + + +//=======MAIN===== +int main(void) +{ + Unity.TestFile = "lfpfunc.c"; + UnityBegin("lfpfunc.c"); + RUN_TEST(test_AdditionLR, 438); + RUN_TEST(test_AdditionRL, 456); + RUN_TEST(test_SubtractionLR, 474); + RUN_TEST(test_SubtractionRL, 488); + RUN_TEST(test_Negation, 505); + + return (UnityEnd()); +}