]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
added first tests for 'l_fp'
authorJuergen Perlinger <perlinger@ntp.org>
Mon, 30 Jun 2014 22:59:29 +0000 (00:59 +0200)
committerJuergen Perlinger <perlinger@ntp.org>
Mon, 30 Jun 2014 22:59:29 +0000 (00:59 +0200)
bk: 53b1ebd122tIGfDlL3oKOjxJc-1ScQ

tests/libntp/Makefile.am
tests/libntp/lfpfunc.cpp [new file with mode: 0644]

index 067e9f01cb22883e4c727afed9f21360f7fb8030..2f19a89fe731d311d42af0995af64f71b0deddd8 100644 (file)
@@ -39,6 +39,7 @@ tests_SOURCES = $(top_srcdir)/sntp/tests_main.cpp     \
                hextoint.cpp            \
                hextolfp.cpp            \
                humandate.cpp           \
+               lfpfunc.cpp             \
                lfptostr.cpp            \
                modetoa.cpp             \
                msyslog.cpp             \
diff --git a/tests/libntp/lfpfunc.cpp b/tests/libntp/lfpfunc.cpp
new file mode 100644 (file)
index 0000000..d675652
--- /dev/null
@@ -0,0 +1,351 @@
+#include "libntptest.h"
+#include "timestructs.h"
+
+extern "C" {
+#include "ntp_fp.h"
+}
+
+#include <float.h>
+#include <math.h>
+
+#include <string>
+#include <sstream>
+
+class lfpTest : public libntptest
+{
+       // nothing new right now
+};
+
+struct lfp_hl {
+       uint32_t h, l;
+};
+
+//----------------------------------------------------------------------
+// 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;
+
+       std::string   toString() const;
+       std::ostream& toStream(std::ostream &oo) const;
+
+       operator double() const;
+       explicit LFP(double);
+
+protected:
+       LFP(const l_fp &rhs);
+
+       l_fp _v;
+};
+
+std::ostream& operator<<(std::ostream &oo, const LFP& rhs)
+{
+       return rhs.toStream(oo);
+}
+
+//::testing::AssertionResult& operator<<(testing::AssertionResult &oo, const LFP &rhs)
+//{
+//     return rhs.toResult(oo);
+//}
+
+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
+{
+       int res;
+
+       res = (_v.l_i > 0) - (_v.l_i < 0);
+       if (!res)
+               res = _v.l_uf != 0;
+       return res;
+}
+
+std::string
+LFP::toString() const
+{
+       std::ostringstream oss;
+       toStream(oss);
+       return oss.str();
+}
+
+std::ostream&
+LFP::toStream(std::ostream &oo) const
+{
+       return oo
+           << 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);
+}
+
+//----------------------------------------------------------------------
+// test support functions
+//----------------------------------------------------------------------
+
+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}}
+};
+static const size_t addsub_cnt(sizeof(addsub_tab)/sizeof(addsub_tab[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 std::max<double>(4.656612873077393e-10, ldexp(fabs(d), -53));
+}
+
+//----------------------------------------------------------------------
+// test addition
+//----------------------------------------------------------------------
+TEST_F(lfpTest, AdditionLR) {
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               LFP op2(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               LFP exp(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               LFP res(op1 + op2);
+
+               ASSERT_EQ(exp, res);
+       }       
+}
+
+TEST_F(lfpTest, AdditionRL) {
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP op2(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               LFP op1(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               LFP exp(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               LFP res(op1 + op2);
+
+               ASSERT_EQ(exp, res);
+       }       
+}
+
+//----------------------------------------------------------------------
+// test subtraction
+//----------------------------------------------------------------------
+TEST_F(lfpTest, SubtractionLR) {
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP op2(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               LFP exp(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               LFP op1(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               LFP res(op1 - op2);
+
+               ASSERT_EQ(exp, res);
+       }       
+}
+
+TEST_F(lfpTest, SubtractionRL) {
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP exp(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               LFP op2(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
+               LFP op1(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
+               LFP res(op1 - op2);
+
+               ASSERT_EQ(exp, res);
+       }       
+}
+
+//----------------------------------------------------------------------
+// test negation
+//----------------------------------------------------------------------
+TEST_F(lfpTest, Negation) {
+       for (size_t idx=0; idx < addsub_cnt; ++idx) {
+               LFP op1(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
+               LFP op2(-op1);
+               LFP sum(op1 + op2);
+
+               ASSERT_EQ(LFP(0,0), sum);
+       }       
+}
+
+//----------------------------------------------------------------------
+// test absolute value
+//----------------------------------------------------------------------
+TEST_F(lfpTest, 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());
+
+               ASSERT_TRUE(op2.signum() >= 0);
+
+               if (op1.signum() >= 0)
+                       op1 -= op2;
+               else
+                       op1 += op2;
+               ASSERT_EQ(LFP(0,0), op1);
+       }       
+}
+
+//----------------------------------------------------------------------
+// fp -> double -> fp rountrip test
+//----------------------------------------------------------------------
+TEST_F(lfpTest, 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));
+       }       
+}
+
+
+//----------------------------------------------------------------------
+// dummy test
+//----------------------------------------------------------------------
+
+TEST_F(lfpTest, dummy) {
+       ASSERT_EQ(1,1);
+}