--- a/src/core/model/int64x64-cairo.h Wed Jan 29 18:58:31 2014 -0800
+++ b/src/core/model/int64x64-cairo.h Wed Jan 29 19:09:35 2014 -0800
@@ -3,35 +3,126 @@
#define INT64X64_CAIRO_H
#include <stdint.h>
-#include <cmath>
+#include <cmath> // pow
#include "cairo-wideint-private.h"
-#ifdef __i386__
-// this assembly code does not appear to work right yet.
-#define noInt64x64_CAIRO_ASM 1
-#endif
namespace ns3 {
+/**
+ * \ingroup core
+ * \defgroup highprec High Precision Q64.64
+ *
+ * Functions and class for high precision Q64.64 fixed point arithmetic.
+ */
+
+/**
+ * \ingroup highprec
+ * High precision numerical type, implementing Q64.64 fixed precision.
+ *
+ * A Q64.64 fixed precision number consists of:
+ *
+ * Bits | Function
+ * ---- | --------
+ * 1 | Sign bit
+ * 63 | Integer portion
+ * 64 | Fractional portion
+ *
+ * All standard arithemetic operations are supported:
+ *
+ * Category | Operators
+ * ----------- | ---------
+ * Computation | `+`, `+=`, `-`, `-=`, `*`, `*=`, `/`, `/=`
+ * Comparison | `==`, `!=`, `<`, `<=`, `>`, `>=`
+ * Unary | `+`, `-`, `!`
+ *
+ */
class int64x64_t
{
+ /// High bit of fractional part
+ static const uint64_t HPCAIRO_MASK_HI_BIT = (((uint64_t)1)<<63);
+ /// Mask for fraction part
+ static const uint64_t HP_MASK_LO = 0xffffffffffffffffULL;
+ /**
+ * Floating point value of HP_MASK_LO + 1
+ * We really want:
+ * \code
+ * static const long double HP_MAX_64 = std:pow (2.0L, 64);
+ * \endcode
+ * but we can't call functions in const definitions,
+ * We could make this a static and initialize in int64x64-cairo.cc or
+ * int64x64.cc, but this requires handling static initialization order
+ * when most of the implementation is inline. Instead, we resort to
+ * this define.
+ */
+#define HP_MAX_64 (std::pow (2.0L, 64))
+
public:
+ /**
+ * Type tag for the underlying implementation.
+ *
+ * A few testcases are are sensitive to implementation,
+ * specifically the double implementation. To handle this,
+ * we expose the underlying implementation type here.
+ */
+ enum impl_type {
+ int128_impl = 0, //!< Native int128_t implementation.
+ cairo_impl = 1, //!< cairo wideint implementation
+ ld_impl = 2 //!< long double implementation
+ };
+
+ /// Type tag for this implementation.
+ static const enum impl_type implementation = cairo_impl;
+
+ /// Default constructor
inline int64x64_t ()
{
_v.hi = 0;
_v.lo = 0;
}
+ /**@{*/
+ /**
+ * Construct from a floating point value.
+ *
+ * \param [in] value floating value to represent
+ */
inline int64x64_t (double value)
{
-#define HPCAIRO_MAX_64 18446744073709551615.0
- double fhi = std::floor (value);
- int64_t hi = lround (fhi);
- uint64_t lo = (uint64_t)((value - fhi) * HPCAIRO_MAX_64);
+ bool sign = value < 0;
+ value = sign ? -value : value;
+ long double hi = std::floor ((long double)value);
+ long double fr = value - hi;
+ long double lo = fr * HP_MAX_64;
_v.hi = hi;
_v.lo = lo;
-#undef HPCAIRO_MAX_64
+ if (sign)
+ {
+ Negate ();
+ }
}
+ inline int64x64_t (long double value)
+ {
+ bool sign = value < 0;
+ value = sign ? -value : value;
+ long double hi = std::floor (value);
+ long double fr = value - hi;
+ long double lo = fr * HP_MAX_64;
+ _v.hi = hi;
+ _v.lo = lo;
+ if (sign)
+ {
+ Negate ();
+ }
+ }
+ /**@}*/
+
+ /**@{*/
+ /**
+ * Construct from an integral type.
+ *
+ * \param [in] v integer value to represent
+ */
inline int64x64_t (int v)
{
_v.hi = v;
@@ -62,70 +153,174 @@
_v.hi = v;
_v.lo = 0;
}
- inline int64x64_t (int64_t hi, uint64_t lo)
+ /**@}*/
+ /**
+ * Construct from explicit high and low values.
+ *
+ * \param [in] hi Integer portion.
+ * \param [in] lo Fractional portion, already scaled to HP_MAX_64.
+ */
+ explicit inline int64x64_t (int64_t hi, uint64_t lo)
{
_v.hi = hi;
_v.lo = lo;
}
- inline int64x64_t (const int64x64_t &o)
+ /**
+ * Copy constructor.
+ *
+ * \param [in] o Value to copy.
+ */
+ inline int64x64_t (const int64x64_t & o)
: _v (o._v) {}
- inline int64x64_t &operator = (const int64x64_t &o)
+ /**
+ * Assignment.
+ *
+ * \param [in] o Value to assign to this int64x64_t.
+ */
+ inline int64x64_t & operator = (const int64x64_t & o)
{
_v = o._v;
return *this;
}
+ /**
+ * Get this value as a double.
+ *
+ * \return This value in floating form.
+ */
inline double GetDouble (void) const
{
-#define HPCAIRO_MAX_64 18446744073709551615.0
- bool is_negative = IsNegative ();
- cairo_int128_t value = is_negative ? _cairo_int128_negate (_v) : _v;
- double flo = value.lo;
- flo /= HPCAIRO_MAX_64;
- double retval = value.hi;
+ bool sign = IsNegative ();
+ cairo_int128_t tmp = sign ? _cairo_int128_negate (_v) : _v;
+ long double flo = tmp.lo / HP_MAX_64;
+ long double retval = tmp.hi;
retval += flo;
- retval = is_negative ? -retval : retval;
+ retval = sign ? -retval : retval;
return retval;
-#undef HPCAIRO_MAX_64
}
+ /**
+ * Get the integer portion.
+ *
+ * \return The integer portion of this value.
+ */
inline int64_t GetHigh (void) const
{
return (int64_t)_v.hi;
}
+ /**
+ * Get the fractional portion of this value, unscaled.
+ *
+ * \return The fractional portion, unscaled, as an integer.
+ */
inline uint64_t GetLow (void) const
{
return _v.lo;
}
- void MulByInvert (const int64x64_t &o);
+ /**
+ * Multiply this value by a Q0.128 value, presumably representing an inverse,
+ * completing a division operation.
+ *
+ * \param [in] o The inverse operand.
+ *
+ * \see Invert
+ */
+ void MulByInvert (const int64x64_t & o);
+ /**
+ * Compute the inverse of an integer value.
+ *
+ * Ordinary division by an integer would be limited to 64 bits of precsion.
+ * Instead, we multiply by the 128-bit inverse of the divisor.
+ * This function computes the inverse to 128-bit precision.
+ * MulByInvert() then completes the division.
+ *
+ * (Really this should be a separate type representing Q0.128.)
+ *
+ * \param [in] v The value to compute the inverse of.
+ * \return A Q0.128 representation of the inverse.
+ */
static int64x64_t Invert (uint64_t v);
private:
- friend bool operator == (const int64x64_t &lhs, const int64x64_t &rhs);
- friend bool operator != (const int64x64_t &lhs, const int64x64_t &rhs);
- friend bool operator <= (const int64x64_t &lhs, const int64x64_t &rhs);
- friend bool operator >= (const int64x64_t &lhs, const int64x64_t &rhs);
- friend bool operator < (const int64x64_t &lhs, const int64x64_t &rhs);
- friend bool operator > (const int64x64_t &lhs, const int64x64_t &rhs);
- friend int64x64_t &operator += (int64x64_t &lhs, const int64x64_t &rhs);
- friend int64x64_t &operator -= (int64x64_t &lhs, const int64x64_t &rhs);
- friend int64x64_t &operator *= (int64x64_t &lhs, const int64x64_t &rhs);
- friend int64x64_t &operator /= (int64x64_t &lhs, const int64x64_t &rhs);
- friend int64x64_t operator + (const int64x64_t &lhs);
- friend int64x64_t operator - (const int64x64_t &lhs);
- friend int64x64_t operator ! (const int64x64_t &lhs);
- void Mul (const int64x64_t &o);
- void Div (const int64x64_t &o);
- static cairo_uint128_t Umul (cairo_uint128_t a, cairo_uint128_t b);
- static cairo_uint128_t Udiv (cairo_uint128_t a, cairo_uint128_t b);
+ friend bool operator == (const int64x64_t & lhs, const int64x64_t & rhs);
+
+ friend bool operator < (const int64x64_t & lhs, const int64x64_t & rhs);
+ friend bool operator <= (const int64x64_t & lhs, const int64x64_t & rhs);
+ friend bool operator > (const int64x64_t & lhs, const int64x64_t & rhs);
+ friend bool operator >= (const int64x64_t & lhs, const int64x64_t & rhs);
+
+ friend int64x64_t & operator += ( int64x64_t & lhs, const int64x64_t & rhs);
+ friend int64x64_t & operator -= ( int64x64_t & lhs, const int64x64_t & rhs);
+ friend int64x64_t & operator *= ( int64x64_t & lhs, const int64x64_t & rhs);
+ friend int64x64_t & operator /= ( int64x64_t & lhs, const int64x64_t & rhs);
+
+ friend int64x64_t operator - (const int64x64_t & lhs);
+ friend int64x64_t operator ! (const int64x64_t & lhs);
+
+ /**
+ * Implement `*=`.
+ *
+ * \param [in] o The other factor.
+ */
+ void Mul (const int64x64_t & o);
+ /**
+ * Implement `/=`.
+ *
+ * \param [in] o The divisor.
+ */
+ void Div (const int64x64_t & o);
+ /**
+ * Unsigned multiplication of Q64.64 values.
+ *
+ * Mathematically this should produce a Q128.128 value;
+ * we keep the central 128 bits, representing the Q64.64 result.
+ * We assert on integer overflow beyond the 64-bit integer portion.
+ *
+ * \param [in] a First factor.
+ * \param [in] b Second factor.
+ * \return The Q64.64 product.
+ *
+ * \internal
+ *
+ * It might be tempting to just use \pname{a} `*` \pname{b}
+ * and be done with it, but it's not that simple. With \pname{a}
+ * and \pname{b} as 128-bit integers, \pname{a} `*` \pname{b}
+ * mathematically produces a 256-bit result, which the computer
+ * truncates to the lowest 128 bits. In our case, where \pname{a}
+ * and \pname{b} are interpreted as Q64.64 fixed point numbers,
+ * the multiplication mathematically produces a Q128.128 fixed point number.
+ * We want the middle 128 bits from the result, truncating both the
+ * high and low 64 bits. To achieve this, we carry out the multiplication
+ * explicitly with 64-bit operands and 128-bit intermediate results.
+ */
+ static cairo_uint128_t Umul (cairo_uint128_t a, cairo_uint128_t b);
+ /**
+ * Unsigned division of Q64.64 values.
+ *
+ * \param [in] a Numerator.
+ * \param [in] b Denominator.
+ * \return The Q64.64 representation of `a / b`
+ */
+ static cairo_uint128_t Udiv (cairo_uint128_t a, cairo_uint128_t b);
+ /**
+ * Unsigned multiplication of Q64.64 and Q0.128 values.
+ *
+ * \param [in] a The numerator, a Q64.64 value.
+ * \param [in] b The inverse of the denominator, a Q0.128 value
+ * \return The product `a * b`, representing the ration `a / b^-1`
+ *
+ * \see Invert
+ */
static cairo_uint128_t UmulByInvert (cairo_uint128_t a, cairo_uint128_t b);
+ /** Negative predicate. */
inline bool IsNegative (void) const
{
- int64_t hi = _v.hi;
- return hi < 0;
+ bool sign = _cairo_int128_negative (_v);;
+ return sign;
}
+ /** Logical negation */
inline void Negate (void)
{
_v.lo = ~_v.lo;
@@ -135,7 +330,13 @@
++_v.hi;
}
}
- inline int Compare (const int64x64_t &o) const
+ /**
+ * Return tri-valued comparision to another value.
+ *
+ * \param [in] o The value to compare to.
+ * \return -1 if `this < o`, 0 if they are equal, and +1 if `this > o`.
+ */
+ inline int Compare (const int64x64_t & o) const
{
int status;
int64x64_t tmp = *this;
@@ -144,112 +345,126 @@
(((tmp)._v.hi == 0 && (tmp)._v.lo == 0)) ? 0 : 1;
return status;
}
- cairo_int128_t _v;
-};
+
+ cairo_int128_t _v; //!< The Q64.64 value.
+
+}; // class int64x64_t
+
-inline bool operator == (const int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Equality operator.
+ */
+inline bool operator == (const int64x64_t & lhs, const int64x64_t & rhs)
{
- return lhs._v.hi == rhs._v.hi && lhs._v.lo == lhs._v.lo;
+ return lhs._v.hi == rhs._v.hi && lhs._v.lo == rhs._v.lo;
}
-
-inline bool operator != (const int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Inequality operator
+ */
+inline bool operator != (const int64x64_t & lhs, const int64x64_t & rhs)
{
return !(lhs == rhs);
}
-
-inline bool operator < (const int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Less than operator
+ */
+inline bool operator < (const int64x64_t & lhs, const int64x64_t & rhs)
{
return lhs.Compare (rhs) < 0;
}
-inline bool operator <= (const int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Less or equal operator
+ */
+inline bool operator <= (const int64x64_t & lhs, const int64x64_t & rhs)
{
return lhs.Compare (rhs) <= 0;
}
-
-inline bool operator >= (const int64x64_t &lhs, const int64x64_t &rhs)
-{
- return lhs.Compare (rhs) >= 0;
-}
-inline bool operator > (const int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Greater operator
+ */
+inline bool operator > (const int64x64_t & lhs, const int64x64_t & rhs)
{
return lhs.Compare (rhs) > 0;
}
-inline int64x64_t &operator += (int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Greater or equal operator
+ */
+inline bool operator >= (const int64x64_t & lhs, const int64x64_t & rhs)
{
-#if Int64x64_CAIRO_ASM
- asm ("mov 0(%1),%%eax\n\t"
- "add %%eax,0(%0)\n\t"
- "mov 4(%1),%%eax\n\t"
- "adc %%eax,4(%0)\n\t"
- "mov 8(%1),%%eax\n\t"
- "adc %%eax,8(%0)\n\t"
- "mov 12(%1),%%eax\n\t"
- "adc %%eax,12(%0)\n\t"
- :
- : "r" (&lhs._v), "r" (&rhs._v)
- : "%eax", "cc");
-#else
- lhs._v.hi += rhs._v.hi;
- lhs._v.lo += rhs._v.lo;
- if (lhs._v.lo < rhs._v.lo)
- {
- lhs._v.hi++;
- }
-#endif
+ return lhs.Compare (rhs) >= 0;
+}
+
+/**
+ * \ingroup highprec
+ * Compound addition operator
+ */
+inline int64x64_t & operator += (int64x64_t & lhs, const int64x64_t & rhs)
+{
+ lhs._v = _cairo_int128_add( lhs._v, rhs._v );
return lhs;
}
-inline int64x64_t &operator -= (int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Compound subtraction operator
+ */
+inline int64x64_t & operator -= (int64x64_t & lhs, const int64x64_t & rhs)
{
-#if Int64x64_CAIRO_ASM
- asm ("mov 0(%1),%%eax\n\t"
- "sub %%eax,0(%0)\n\t"
- "mov 4(%1),%%eax\n\t"
- "sbb %%eax,4(%0)\n\t"
- "mov 8(%1),%%eax\n\t"
- "sbb %%eax,8(%0)\n\t"
- "mov 12(%1),%%eax\n\t"
- "sbb %%eax,12(%0)\n\t"
- :
- : "r" (&lhs._v), "r" (&rhs._v)
- : "%eax", "cc");
-#else
- lhs._v.hi -= rhs._v.hi;
- lhs._v.lo -= rhs._v.lo;
- if (lhs._v.lo > rhs._v.lo)
- {
- lhs._v.hi--;
- }
-#endif
+ lhs._v = _cairo_int128_sub( lhs._v, rhs._v );
return lhs;
}
-inline int64x64_t &operator *= (int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Compound multiplication operator
+ */
+inline int64x64_t & operator *= (int64x64_t & lhs, const int64x64_t & rhs)
{
lhs.Mul (rhs);
return lhs;
}
-inline int64x64_t &operator /= (int64x64_t &lhs, const int64x64_t &rhs)
+/**
+ * \ingroup highprec
+ * Compound division operator
+ */
+inline int64x64_t & operator /= (int64x64_t & lhs, const int64x64_t & rhs)
{
lhs.Div (rhs);
return lhs;
}
-inline int64x64_t operator + (const int64x64_t &lhs)
+/**
+ * \ingroup highprec
+ * Unary plus operator
+ */
+inline int64x64_t operator + (const int64x64_t & lhs)
{
return lhs;
}
-
-inline int64x64_t operator - (const int64x64_t &lhs)
+/**
+ * \ingroup highprec
+ * Unary negation operator (change sign operator)
+ */
+inline int64x64_t operator - (const int64x64_t & lhs)
{
int64x64_t tmp = lhs;
tmp.Negate ();
return tmp;
}
-
-inline int64x64_t operator ! (const int64x64_t &lhs)
+/**
+ * \ingroup highprec
+ * Logical not operator
+ */
+inline int64x64_t operator ! (const int64x64_t & lhs)
{
return (lhs._v.hi == 0 && lhs._v.lo == 0) ? int64x64_t (1, 0) : int64x64_t ();
}
+
} // namespace ns3
#endif /* INT64X64_CAIRO_H */
--- a/src/core/test/int64x64-test-suite.cc Wed Jan 29 18:58:31 2014 -0800
+++ b/src/core/test/int64x64-test-suite.cc Wed Jan 29 19:09:35 2014 -0800
@@ -5,35 +5,76 @@
using namespace ns3;
-class Int64x64FracTestCase : public TestCase
+/**
+ * \internal
+ *
+ * Some of these tests are a little unusual for ns-3 in that they
+ * are sensitive to implementation, specifically the double implementation.
+ * To handle this, where needed we define a tolerance to use in the
+ * test comparisions. If you need to increase the tolerance,
+ * please append the system and compiler version. For example:
+ *
+ * \code
+ * // Darwin 12.5.0 (Mac 10.8.5) g++ 4.2.1
+ * tolerance = 1;
+ * // System Foo gcc 3.9
+ * tolerance = 3;
+ * \endcode
+ */
+class Int64x64HiLoTestCase : public TestCase
{
public:
- Int64x64FracTestCase ();
+ Int64x64HiLoTestCase ();
virtual void DoRun (void);
- void CheckFrac (int64_t hi, uint64_t lo);
+ void Check (const int64_t hi, const uint64_t lo,
+ const int64_t loTolerance = 0);
};
void
-Int64x64FracTestCase::CheckFrac (int64_t hi, uint64_t lo)
+Int64x64HiLoTestCase::Check (const int64_t hi,
+ const uint64_t lo,
+ const int64_t loTolerance /* = 0 */)
{
int64x64_t tmp = int64x64_t (hi,lo);
+
+ std::cout << GetParent ()->GetName () << " Check "
+ << std::hex << std::setfill ('0')
+ << "hi: 0x" << std::setw (16) << hi
+ << " lo: 0x" << std::setw (16) << lo
+ << std::dec << std::setfill (' ')
+ << " int64x64: " << tmp
+ << std::endl;
+
NS_TEST_EXPECT_MSG_EQ (tmp.GetHigh (), hi,
- "High part does not match");
- NS_TEST_EXPECT_MSG_EQ (tmp.GetLow (), lo,
- "Low part does not match");
+ "High part does not match for hi:" << hi
+ << " lo: " << lo);
+ NS_TEST_EXPECT_MSG_EQ_TOL ((int64_t)tmp.GetLow (), (int64_t)lo, loTolerance,
+ "Low part does not match for hi: " << hi
+ << " lo: " << lo);
}
-Int64x64FracTestCase::Int64x64FracTestCase ()
- : TestCase ("Check that we can manipulate the high and low part of every number")
+Int64x64HiLoTestCase::Int64x64HiLoTestCase ()
+ : TestCase ("Manipulate the high and low part of every number")
{
}
void
-Int64x64FracTestCase::DoRun (void)
+Int64x64HiLoTestCase::DoRun (void)
{
- CheckFrac (1, 0);
- CheckFrac (1, 1);
- CheckFrac (-1, 0);
- CheckFrac (-1, 1);
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
+ int64_t tolerance = 0;
+ if (int64x64_t::implementation == int64x64_t::ld_impl)
+ {
+ // Darwin 12.5.0 (Mac 10.8.5) g++ 4.2.1
+ tolerance = 1;
+ }
+
+ Check (1, 0);
+ Check (1, 1, tolerance);
+ Check (-1, 0);
+ Check (-1, 1);
}
@@ -42,39 +83,71 @@
public:
Int64x64InputTestCase ();
virtual void DoRun (void);
- void CheckString (std::string str, int64_t hi, uint64_t lo);
+ void Check (const std::string & str,
+ const int64_t hi, const uint64_t lo,
+ const int64_t loTolerance = 0);
};
Int64x64InputTestCase::Int64x64InputTestCase ()
- : TestCase ("Check that we parse Int64x64 numbers as strings")
+ : TestCase ("Parse int64x64_t numbers as strings")
{
}
void
-Int64x64InputTestCase::CheckString (std::string str, int64_t hi, uint64_t lo)
+Int64x64InputTestCase::Check (const std::string & str,
+ const int64_t hi, const uint64_t lo,
+ const int64_t loTolerance /* = 0 */)
+
{
std::istringstream iss;
iss.str (str);
int64x64_t hp;
iss >> hp;
- NS_TEST_EXPECT_MSG_EQ (hp.GetHigh (), hi, "High parts do not match for input string " << str);
- NS_TEST_EXPECT_MSG_EQ (hp.GetLow (), lo, "Low parts do not match for input string " << str);
+
+ std::string input = "\"" + str + "\"";
+
+ std::cout << GetParent ()->GetName () << " Input: "
+ << std::left << std::setw (28) << input << std::right
+ << std::hex << std::setfill ('0')
+ << " hi: 0x" << std::setw (16) << hp.GetHigh ()
+ << " lo: 0x" << std::setw (16) << hp.GetLow ()
+ << std::dec << std::setfill (' ')
+ << " expected: " << hi << " " << lo << " ± " << loTolerance
+ << std::endl;
+
+ NS_TEST_EXPECT_MSG_EQ (hp.GetHigh (), hi,
+ "High parts do not match for input string \""
+ << str << "\"");
+ NS_TEST_EXPECT_MSG_EQ_TOL ((int64_t)hp.GetLow (), (int64_t)lo, loTolerance,
+ "Low parts do not match for input string \""
+ << str << "\"");
}
void
Int64x64InputTestCase::DoRun (void)
{
- CheckString ("1", 1, 0);
- CheckString ("+1", 1, 0);
- CheckString ("-1", -1, 0);
- CheckString ("1.0", 1, 0);
- CheckString ("+1.0", 1, 0);
- CheckString ("001.0", 1, 0);
- CheckString ("+001.0", 1, 0);
- CheckString ("020.0", 20, 0);
- CheckString ("+020.0", 20, 0);
- CheckString ("-1.0", -1, 0);
- CheckString ("-1.0000", -1, 0);
- CheckString ("1.0000000", 1, 0);
- CheckString (" 1.000000000000000000054", 1, 1);
- CheckString ("-1.000000000000000000054", -1, 1);
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
+ int64_t tolerance = 0;
+ if (int64x64_t::implementation == int64x64_t::ld_impl)
+ {
+ // Darwin 12.5.0 (Mac 10.8.5) g++ 4.2.1
+ tolerance = 2;
+ }
+
+ Check ("1", 1, 0);
+ Check ("+1", 1, 0);
+ Check ("-1", -1, 0);
+ Check ("1.0", 1, 0);
+ Check ("+1.0", 1, 0);
+ Check ("001.0", 1, 0);
+ Check ("+001.0", 1, 0);
+ Check ("020.0", 20, 0);
+ Check ("+020.0", 20, 0);
+ Check ("1.0000000", 1, 0);
+ Check ("-1.0", -1, 0, tolerance);
+ Check ("-1.0000", -1, 0, tolerance);
+ Check (" 1.000000000000000000054", 1, 1, tolerance);
+ Check ("-1.000000000000000000054", -1, 1, tolerance);
}
class Int64x64InputOutputTestCase : public TestCase
@@ -82,14 +155,16 @@
public:
Int64x64InputOutputTestCase ();
virtual void DoRun (void);
- void CheckString (std::string str);
+ void Check (const std::string & str,
+ const int64_t tolerance = 0);
};
Int64x64InputOutputTestCase::Int64x64InputOutputTestCase ()
- : TestCase ("Check that we can roundtrip Int64x64 numbers as strings")
+ : TestCase ("Roundtrip int64x64_t numbers as strings")
{
}
void
-Int64x64InputOutputTestCase::CheckString (std::string str)
+Int64x64InputOutputTestCase::Check (const std::string & str,
+ const int64_t tolerance /* = 0 */)
{
std::istringstream iss;
iss.str (str);
@@ -97,80 +172,185 @@
iss >> value;
std::ostringstream oss;
oss << std::scientific << std::setprecision (21) << value;
- NS_TEST_EXPECT_MSG_EQ (oss.str (), str, "Converted string does not match expected string");
+
+ std::string input = "\"" + str + "\"";
+ std::string output = "\"" + oss.str () + "\"";
+
+ std::cout << GetParent ()->GetName () << " InputOutput: "
+ << " in: " << std::left << std::setw (28) << input << std::right
+ << " out: " << std::left << std::setw (28) << output << std::right
+ << std::endl;
+
+ if (tolerance == 0)
+ {
+ NS_TEST_EXPECT_MSG_EQ (oss.str (), str,
+ "Converted string does not match expected string");
+ }
+ else
+ {
+ // No obvious way to implement a tolerance
+ }
}
void
Int64x64InputOutputTestCase::DoRun (void)
{
- CheckString ("+1.000000000000000000000");
- CheckString ("+0.000000000000000000000");
- CheckString ("-1.000000000000000000000");
- CheckString ("+20.000000000000000000000");
- CheckString ("+1.084467440737095516158");
- CheckString ("-1.084467440737095516158");
- CheckString ("+1.184467440737095516179");
- CheckString ("-1.184467440737095516179");
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
+ int64_t tolerance = 0;
+ if (int64x64_t::implementation == int64x64_t::ld_impl)
+ {
+ // Darwin 12.5.0 (Mac 10.8.5) g++ 4.2.1
+ tolerance = 1;
+ }
+
+ Check ("+1.000000000000000000000");
+ Check ("+20.000000000000000000000");
+ Check ("+0.000000000000000000000", tolerance);
+ Check ("-1.000000000000000000000", tolerance);
+ Check ("+1.084467440737095516158", tolerance);
+ Check ("-2.084467440737095516158", tolerance);
+ Check ("+3.184467440737095516179", tolerance);
+ Check ("-4.184467440737095516179", tolerance);
}
-#define CHECK_EXPECTED(a,b) \
- NS_TEST_ASSERT_MSG_EQ ((a).GetHigh (),b,"Arithmetic failure: " << ((a).GetHigh ()) << "!=" << (b))
-
-#define V(v) \
- int64x64_t (v)
class Int64x64ArithmeticTestCase : public TestCase
{
public:
Int64x64ArithmeticTestCase ();
virtual void DoRun (void);
+ void Check (const int test,
+ const int64x64_t value, const int64x64_t expected);
+ void Check (const int test,
+ const int64x64_t value, const int64x64_t expected,
+ const int64x64_t tolerance);
};
Int64x64ArithmeticTestCase::Int64x64ArithmeticTestCase ()
- : TestCase ("Check basic arithmetic operations")
+ : TestCase ("Basic arithmetic operations")
{
}
void
+Int64x64ArithmeticTestCase::Check (const int test,
+ const int64x64_t value,
+ const int64x64_t expected)
+{
+ int64x64_t zero (0,0);
+ Check (test, value, expected, zero);
+}
+void
+Int64x64ArithmeticTestCase::Check (const int test,
+ const int64x64_t value,
+ const int64x64_t expected,
+ const int64x64_t tolerance)
+{
+ std::cout << GetParent ()->GetName () << " Arithmetic: "
+ << test << ": " << value << " ?= " << expected
+ << " (+/- " << tolerance << ")"
+ << std::endl;
+
+ NS_TEST_ASSERT_MSG_EQ_TOL ( value, expected, tolerance,
+ "Arithmetic failure in test case " << test);
+}
+
+void
Int64x64ArithmeticTestCase::DoRun (void)
{
- int64x64_t a, b;
+ const int64x64_t zero (0, 0);
+ const int64x64_t one (1, 0);
+ const int64x64_t two (2, 0);
+ const int64x64_t thre (3, 0);
+
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
+ Check ( 0, zero - zero , zero );
+ Check ( 1, zero - one , -one );
+ Check ( 2, one - one , zero );
+ Check ( 3, one - two , -one );
+ Check ( 4, one - (-one ), two );
+ Check ( 5, (-one ) - (-two ), one );
+ Check ( 6, (-one ) - two , -thre );
+
+ Check ( 7, zero + zero , zero );
+ Check ( 8, zero + one , one );
+ Check ( 9, one + one , two );
+ Check (10, one + two , thre );
+ Check (11, one + (-one ), zero );
+ Check (12, (-one ) + (-two ), -thre );
+ Check (13, (-one ) + two , one );
+
+ Check (14, zero * zero , zero );
+ Check (15, zero * one , zero );
+ Check (16, zero * (-one ), zero );
+ Check (17, one * one , one );
+ Check (18, one * (-one ), -one );
+ Check (19, (-one ) * (-one ), one );
+
+ Check (20, (two * thre ) / thre , two );
- CHECK_EXPECTED (V (1) - V (1), 0);
- CHECK_EXPECTED (V (1) - V (2), -1);
- CHECK_EXPECTED (V (1) - V (3), -2);
- CHECK_EXPECTED (V (1) - V (-1), 2);
- CHECK_EXPECTED (V (1) - V (-2), 3);
- CHECK_EXPECTED (V (-3) - V (-4), 1);
- CHECK_EXPECTED (V (-2) - V (3), -5);
- CHECK_EXPECTED (V (1) + V (2), 3);
- CHECK_EXPECTED (V (1) + V (-3), -2);
- CHECK_EXPECTED (V (0) + V (0), 0);
- CHECK_EXPECTED (V (0) * V (0), 0);
- CHECK_EXPECTED (V (0) * V (1), 0);
- CHECK_EXPECTED (V (0) * V (-1), 0);
- CHECK_EXPECTED (V (1) * V (0), 0);
- CHECK_EXPECTED (V (1) * V (1), 1);
- CHECK_EXPECTED (V (1) * V (-1), -1);
- CHECK_EXPECTED (V (-1) * V (-1), 1);
- CHECK_EXPECTED (V (0) * V (1), 0);
- CHECK_EXPECTED (V (0) * V (-1), 0);
- CHECK_EXPECTED (V (-1) * V (1), -1);
+ const int64x64_t frac = int64x64_t (0, 0xc000000000000000ULL); // 0.75
+ const int64x64_t fplf2 = frac + frac * frac; // 1.3125
+ NS_TEST_ASSERT_MSG_EQ (frac, 0.75, "Arithmetic failure fractional part");
+ NS_TEST_ASSERT_MSG_EQ (fplf2, 1.3125, "Arithmetic failure fplf2");
+
+ const int64x64_t zerof = zero + frac;
+ NS_TEST_ASSERT_MSG_EQ (zerof, frac, "Arithmetic failure adding fractional part");
+ const int64x64_t onef = one + frac;
+ const int64x64_t twof = two + frac;
+ const int64x64_t thref = thre + frac;
+
+ Check (21, zerof - zerof, zero );
+ Check (22, zerof - onef, -one );
+ Check (23, onef - onef, zero );
+ Check (24, onef - twof, -one );
+ Check (25, onef - (-onef), twof + frac );
+ Check (26, (-onef) - (-twof), one );
+ Check (27, (-onef) - twof, -thref - frac );
+
+ Check (28, zerof + zerof, zerof + frac );
+ Check (29, zerof + onef, onef + frac );
+ Check (30, onef + onef, twof + frac );
+ Check (31, onef + twof, thref + frac );
+ Check (32, onef + (-onef), zero );
+ Check (33, (-onef) + (-twof), -thref - frac );
+ Check (34, (-onef) + twof, one );
+
+ Check (35, zerof * zerof, frac * frac );
+ Check (36, zero * onef, zero );
+ Check (37, zerof * one, frac );
+
+ int64x64_t foo = zerof * onef;
+
+ Check (38, zerof * onef, fplf2 );
+ Check (39, zerof * (-onef), -fplf2 );
+ Check (40, onef * onef, onef + fplf2 );
+ Check (41, onef * (-onef), -onef - fplf2 );
+ Check (42, (-onef) * (-onef), onef + fplf2 );
+
+
+ // Multiplication followed by division is exact:
+ Check (43, (two * thre ) / thre , two );
+ Check (44, (twof * thref) / thref, twof );
-
- CHECK_EXPECTED (V (2) * V (3) / V (3), 2);
-
- // Below, the division loses precision because 2/3 is not
- // representable exactly in 64.64 integers. So, we got
- // something super close but the final rounding kills us.
- a = V (2);
- b = V (3);
- a /= b;
- a *= b;
- CHECK_EXPECTED (V (2) / V (3) * V (3), 1);
+ // Division followed by multiplication loses a bit or two:
+ int64x64_t tolerance (0, 3);
+ Check (45, (two / thre) * thre, two , tolerance );
+ Check (46, (twof / thref) * thref, twof, tolerance );
// The example below shows that we really do not lose
// much precision internally: it is almost always the
// final conversion which loses precision.
- CHECK_EXPECTED (V (2000000000) / V (3) * V (3), 1999999999);
+ Check (47, (int64x64_t (2000000000) / int64x64_t (3)) * int64x64_t (3),
+ int64x64_t (1999999999, 0xfffffffffffffffeULL));
+
+ // Check special values
+ Check (48, int64x64_t (0, 0x159fa87f8aeaad21ULL) * 10,
+ int64x64_t (0, 0xd83c94fb6d2ac34aULL));
+
}
/**
@@ -181,6 +361,8 @@
public:
Int64x64Bug455TestCase ();
virtual void DoRun (void);
+ void Check (const double result, const double expect,
+ const std::string & msg);
};
Int64x64Bug455TestCase::Int64x64Bug455TestCase ()
@@ -188,23 +370,44 @@
{
}
void
+Int64x64Bug455TestCase::Check (const double result, const double expect,
+ const std::string & msg)
+{
+ std::cout << GetParent ()->GetName () << " Bug 455: "
+ << "res: " << result
+ << " exp: " << expect
+ << ": " << msg
+ << std::endl;
+
+ NS_TEST_ASSERT_MSG_EQ (result, expect, msg);
+}
+
+void
Int64x64Bug455TestCase::DoRun (void)
{
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
int64x64_t a = int64x64_t (0.1);
a /= int64x64_t (1.25);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 0.08, "The original testcase");
+ Check (a.GetDouble (), 0.08, "The original testcase");
+
a = int64x64_t (0.5);
a *= int64x64_t (5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 2.5, "Simple test for multiplication");
+ Check (a.GetDouble (), 2.5, "Simple test for multiplication");
+
a = int64x64_t (-0.5);
a *= int64x64_t (5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -2.5, "Test sign, first operation negative");
+ Check (a.GetDouble (), -2.5, "Test sign, first operation negative");
+
a = int64x64_t (-0.5);
a *=int64x64_t (-5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 2.5, "both operands negative");
+ Check (a.GetDouble (), 2.5, "both operands negative");
+
a = int64x64_t (0.5);
a *= int64x64_t (-5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -2.5, "only second operand negative");
+ Check (a.GetDouble (), -2.5, "only second operand negative");
}
/**
@@ -215,6 +418,8 @@
public:
Int64x64Bug863TestCase ();
virtual void DoRun (void);
+ void Check (const double result, const double expect,
+ const std::string & msg);
};
Int64x64Bug863TestCase::Int64x64Bug863TestCase ()
@@ -222,24 +427,46 @@
{
}
void
+Int64x64Bug863TestCase::Check (const double result, const double expect,
+ const std::string & msg)
+{
+ std::cout << GetParent ()->GetName () << " Bug 863: "
+ << "res: " << result
+ << " exp: " << expect
+ << ": " << msg
+ << std::endl;
+
+ NS_TEST_ASSERT_MSG_EQ (result, expect, msg);
+}
+
+void
Int64x64Bug863TestCase::DoRun (void)
{
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
int64x64_t a = int64x64_t (0.9);
a /= int64x64_t (1);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 0.9, "The original testcase");
+ Check (a.GetDouble (), 0.9, "The original testcase");
+
a = int64x64_t (0.5);
a /= int64x64_t (0.5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 1.0, "Simple test for division");
+ Check (a.GetDouble (), 1.0, "Simple test for division");
+
a = int64x64_t (-0.5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -0.5, "Check that we actually convert doubles correctly");
+ Check (a.GetDouble (), -0.5, "Check that we actually convert doubles correctly");
+
a /= int64x64_t (0.5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -1.0, "first argument negative");
+ Check (a.GetDouble (), -1.0, "first argument negative");
+
a = int64x64_t (0.5);
a /= int64x64_t (-0.5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), -1.0, "second argument negative");
+ Check (a.GetDouble (), -1.0, "second argument negative");
+
a = int64x64_t (-0.5);
a /= int64x64_t (-0.5);
- NS_TEST_ASSERT_MSG_EQ (a.GetDouble (), 1.0, "both arguments negative");
+ Check (a.GetDouble (), 1.0, "both arguments negative");
}
/**
@@ -249,8 +476,9 @@
{
public:
Int64x64Bug1786TestCase ();
- void Check (const uint64_t low, const std::string & value);
virtual void DoRun (void);
+ void Check (const uint64_t low, const std::string & value,
+ const int64_t tolerance = 0);
};
Int64x64Bug1786TestCase::Int64x64Bug1786TestCase ()
@@ -258,22 +486,43 @@
{
}
void
-Int64x64Bug1786TestCase::Check (const uint64_t low, const std::string & str)
+Int64x64Bug1786TestCase::Check (const uint64_t low,
+ const std::string & str,
+ const int64_t tolerance /* = 0 */)
{
int64x64_t value (0, low);
std::ostringstream oss;
oss << std::scientific << std::setprecision (21) << value;
- std::cout << " 0x" << std::hex << std::setw (16) << low << std::dec
+
+ std::cout << GetParent ()->GetName () << " Bug 1786: "
+ << " 0x" << std::hex << std::setw (16) << low << std::dec
<< " = " << oss.str ()
<< std::endl;
- NS_TEST_EXPECT_MSG_EQ (oss.str (), str, "Fraction string not correct");
+
+ if (tolerance == 0)
+ {
+ NS_TEST_EXPECT_MSG_EQ (oss.str (), str,
+ "Fraction string not correct");
+ }
+ else
+ {
+ // No obvious way to implement a tolerance
+ }
}
void
Int64x64Bug1786TestCase::DoRun (void)
{
std::cout << std::endl;
- std::cout << GetName () << ":" << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+ int64_t tolerance = 0;
+ if (int64x64_t::implementation == int64x64_t::ld_impl)
+ {
+ // Darwin 12.5.0 (Mac 10.8.5) g++ 4.2.1
+ tolerance = 1;
+ }
+
Check ( 1ULL, "+0.000000000000000000054");
Check ( 2ULL, "+0.000000000000000000108");
Check ( 3ULL, "+0.000000000000000000162");
@@ -299,13 +548,13 @@
Check ( 0xF000000000000ULL, "+0.000228881835937500000");
Check ( 0xF0000000000000ULL, "+0.003662109375000000000");
std::cout << std::endl;
- Check (0x7FFFFFFFFFFFFFFDULL, "+0.499999999999999999837");
- Check (0x7FFFFFFFFFFFFFFEULL, "+0.499999999999999999891");
- Check (0x7FFFFFFFFFFFFFFFULL, "+0.499999999999999999945");
+ Check (0x7FFFFFFFFFFFFFFDULL, "+0.499999999999999999837", tolerance);
+ Check (0x7FFFFFFFFFFFFFFEULL, "+0.499999999999999999891", tolerance);
+ Check (0x7FFFFFFFFFFFFFFFULL, "+0.499999999999999999945", tolerance);
Check (0x8000000000000000ULL, "+0.500000000000000000000");
- Check (0x8000000000000001ULL, "+0.500000000000000000054");
- Check (0x8000000000000002ULL, "+0.500000000000000000108");
- Check (0x8000000000000003ULL, "+0.500000000000000000162");
+ Check (0x8000000000000001ULL, "+0.500000000000000000054", tolerance);
+ Check (0x8000000000000002ULL, "+0.500000000000000000108", tolerance);
+ Check (0x8000000000000003ULL, "+0.500000000000000000162", tolerance);
std::cout << std::endl;
Check (0xF000000000000000ULL, "+0.937500000000000000000");
Check (0xFF00000000000000ULL, "+0.996093750000000000000");
@@ -322,8 +571,7 @@
Check (0xFFFFFFFFFFFFF000ULL, "+0.999999999999999777955");
Check (0xFFFFFFFFFFFFFF00ULL, "+0.999999999999999986122");
Check (0xFFFFFFFFFFFFFFF0ULL, "+0.999999999999999999132");
- Check (0xFFFFFFFFFFFFFFFFULL, "+0.999999999999999999945");
-
+ Check (0xFFFFFFFFFFFFFFFFULL, "+0.999999999999999999945", tolerance);
}
class Int64x64CompareTestCase : public TestCase
@@ -331,21 +579,98 @@
public:
Int64x64CompareTestCase ();
virtual void DoRun (void);
+ void Check (const bool result, const bool expected,
+ const std::string & msg);
};
Int64x64CompareTestCase::Int64x64CompareTestCase ()
- : TestCase ("Check basic compare operations")
+ : TestCase ("Basic compare operations")
{
}
void
+Int64x64CompareTestCase::Check (const bool result, const bool expected,
+ const std::string & msg)
+{
+ std::cout << GetParent ()->GetName () << " Compare: "
+ << (result == expected ? "pass: " : "FAIL: ") << msg
+ << std::endl;
+
+ NS_TEST_ASSERT_MSG_EQ (result, expected, msg);
+}
+
+void
Int64x64CompareTestCase::DoRun (void)
{
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
- NS_TEST_ASSERT_MSG_EQ ((V (-1) < V (1)), true, "a is smaller than b");
- NS_TEST_ASSERT_MSG_EQ ((V (-1) > V (-2)), true, "a is bigger than b");
- NS_TEST_ASSERT_MSG_EQ ((V (-1) == V (-1)), true, "a is equal to b");
+ const int64x64_t zero ( 0, 0);
+ const int64x64_t one ( 1, 0);
+ const int64x64_t two ( 2, 0);
+ const int64x64_t mone (-1, 0);
+ const int64x64_t mtwo (-2, 0);
+ const int64x64_t frac = int64x64_t (0, 0xc000000000000000ULL); // 0.75
+ const int64x64_t zerof = zero + frac;
+ const int64x64_t onef = one + frac;
+ const int64x64_t twof = two + frac;
+ const int64x64_t monef = mone - frac;
+ const int64x64_t mtwof = mtwo - frac;
+
+ Check ( zerof == zerof, true, "equality, zero");
+ Check ( onef == onef, true, "equality, positive");
+ Check ( mtwof == mtwof, true, "equality, negative");
+ Check ( zero == one, false, "equality false, zero");
+ Check ( one == two, false, "equality false, unsigned");
+ Check ( one == mone, false, "equality false, signed");
+ Check ( onef == one, false, "equality false, fraction");
+ std::cout << std::endl;
+
+ Check ( zerof != zerof, false, "inequality, zero");
+ Check ( onef != onef, false, "inequality, positive");
+ Check ( mtwof != mtwof, false, "inequality, negative");
+ Check ( zero != one, true, "inequality true, zero");
+ Check ( one != two, true, "inequality true, unsigned");
+ Check ( one != mone, true, "inequality true, signed");
+ Check ( onef != one, true, "inequality true, fraction");
+ std::cout << std::endl;
- NS_TEST_ASSERT_MSG_EQ ((V (1) > V (-1)), true, "a is bigger than b");
- NS_TEST_ASSERT_MSG_EQ ((V (1) < V (2)), true, "a is smaller than b");
+ Check ( zerof < onef, true, "less, zerof");
+ Check ( zero < zerof, true, "less, zero");
+ Check ( one < onef, true, "less, positive");
+ Check ( monef < mone, true, "less, negative");
+ Check ( onef < one, false, "less, false, positive");
+ Check ( mtwo < mtwof, false, "less, false, negative");
+ std::cout << std::endl;
+
+ Check ( zerof <= zerof, true, "less equal, equal, zerof");
+ Check ( zero <= zerof, true, "less equal, less, zero");
+ Check ( onef <= onef, true, "less equal, equal, positive");
+ Check ( monef <= mone, true, "less equal, less, negative");
+ Check ( onef <= one, false, "less equal, false, positive");
+ Check ( mtwo <= mtwof, false, "less equal, false, negative");
+ std::cout << std::endl;
+
+ Check ( onef > zerof, true, "greater, zerof");
+ Check ( zerof > zero, true, "greater, zero");
+ Check ( onef > one, true, "greater, positive");
+ Check ( mone > monef, true, "greater, negative");
+ Check ( one > onef, false, "greater, false, positive");
+ Check ( mtwof > mtwo, false, "greater, false, negative");
+ std::cout << std::endl;
+
+ Check ( zerof >= zerof, true, "greater equal, equal, zerof");
+ Check ( zerof >= zero, true, "greater equal, greater, zero");
+ Check ( onef >= onef, true, "greater equal, equal, positive");
+ Check ( mone >= monef, true, "greater equal, greater, negative");
+ Check ( one >= onef, false, "greater equal, false, positive");
+ Check ( mtwof >= mtwo, false, "greater equal, false, negative");
+ std::cout << std::endl;
+
+ Check ( (!zero) == one, true, "not zero");
+ Check ( (!zerof) == zero, true, "not zerof");
+ Check ( (!one) == zero, true, "not one");
+ Check ( (+onef) == onef, true, "unary positive");
+ Check ( (-onef) == monef, true, "unary negative");
}
class Int64x64InvertTestCase : public TestCase
@@ -353,61 +678,104 @@
public:
Int64x64InvertTestCase ();
virtual void DoRun (void);
+ void Check (const int64_t factor);
+ void CheckCase (const uint64_t factor,
+ const int64x64_t result, const int64x64_t expected,
+ const std::string & msg,
+ const double tolerance = 0);
};
Int64x64InvertTestCase::Int64x64InvertTestCase ()
- : TestCase ("Test case for invertion")
+ : TestCase ("Invert and MulByInvert")
+{
+}
+void
+Int64x64InvertTestCase::CheckCase (const uint64_t factor,
+ const int64x64_t result,
+ const int64x64_t expected,
+ const std::string & msg,
+ const double tolerance /* = 0 */)
{
+ std::cout << GetParent ()->GetName () << " Invert: "
+ << factor << ": ";
+ if (result == expected)
+ {
+ std::cout << "pass: ";
+ }
+ else
+ {
+ std::cout << "FAIL: "
+ << "(res: " << result
+ << " exp: " << expected
+ << " tol: " << tolerance << ") ";
+ }
+ std::cout << msg
+ << std::endl;
+
+ NS_TEST_ASSERT_MSG_EQ_TOL (result, expected, int64x64_t(tolerance), msg);
+}
+
+void
+Int64x64InvertTestCase::Check (const int64_t factor)
+{
+ const int64x64_t one (1, 0);
+ const int64x64_t factorI = one / int64x64_t (factor);
+
+ const int64x64_t a = int64x64_t::Invert (factor);
+ int64x64_t b = int64x64_t (factor);
+
+ double tolerance = 0;
+ if (int64x64_t::implementation == int64x64_t::ld_impl)
+ {
+ // Darwin 12.5.0 (Mac 10.8.5) g++ 4.2.1
+ tolerance = 0.000000000000000001L;
+ }
+
+ b.MulByInvert (a);
+ CheckCase (factor, b, one, "x * x^-1 == 1", tolerance);
+
+ int64x64_t c = int64x64_t (1);
+ c.MulByInvert (a);
+ CheckCase (factor, c, factorI, "1 * x^-1 == 1 / x");
+
+ int64x64_t d = int64x64_t (1);
+ d /= (int64x64_t (factor));
+ CheckCase (factor, d, c, "1/x == x^-1");
+
+ int64x64_t e = int64x64_t (-factor);
+ e.MulByInvert (a);
+ CheckCase (factor, e, -one, "-x * x^-1 == -1", tolerance);
}
void
Int64x64InvertTestCase::DoRun (void)
{
-#define TEST(factor) \
- do { \
- int64x64_t a; \
- a = int64x64_t::Invert (factor); \
- int64x64_t b = V (factor); \
- b.MulByInvert (a); \
- NS_TEST_ASSERT_MSG_EQ (b.GetHigh (), 1, \
- "x * 1/x should be 1 for x=" << factor); \
- int64x64_t c = V (1); \
- c.MulByInvert (a); \
- NS_TEST_ASSERT_MSG_EQ (c.GetHigh (), 0, \
- "1 * 1/x should be 0 for x=" << factor); \
- int64x64_t d = V (1); \
- d /= (V (factor)); \
- NS_TEST_ASSERT_MSG_EQ (d.GetDouble (), c.GetDouble (), \
- "1 * 1/x should be equal to 1/x for x=" << factor); \
- int64x64_t e = V (-factor); \
- e.MulByInvert (a); \
- NS_TEST_ASSERT_MSG_EQ (e.GetHigh (), -1, \
- "-x * 1/x should be -1 for x=" << factor); \
- } \
- while(false)
- TEST (2);
- TEST (3);
- TEST (4);
- TEST (5);
- TEST (6);
- TEST (10);
- TEST (99);
- TEST (100);
- TEST (1000);
- TEST (10000);
- TEST (100000);
- TEST (100000);
- TEST (1000000);
- TEST (10000000);
- TEST (100000000);
- TEST (1000000000);
- TEST (10000000000LL);
- TEST (100000000000LL);
- TEST (1000000000000LL);
- TEST (10000000000000LL);
- TEST (100000000000000LL);
- TEST (1000000000000000LL);
-#undef TEST
+ std::cout << std::endl;
+ std::cout << GetParent ()->GetName () << ": " << GetName () << ":"
+ << std::endl;
+
+ Check (2);
+ Check (3);
+ Check (4);
+ Check (5);
+ Check (6);
+ Check (10);
+ Check (99);
+ Check (100);
+ Check (1000);
+ Check (10000);
+ Check (100000);
+ Check (100000);
+ Check (1000000);
+ Check (10000000);
+ Check (100000000);
+ Check (1000000000);
+ Check (10000000000LL);
+ Check (100000000000LL);
+ Check (1000000000000LL);
+ Check (10000000000000LL);
+ Check (100000000000000LL);
+ Check (1000000000000000LL);
}
@@ -418,14 +786,14 @@
Int64x64128TestSuite ()
: TestSuite ("int64x64", UNIT)
{
- AddTestCase (new Int64x64FracTestCase (), TestCase::QUICK);
+ AddTestCase (new Int64x64HiLoTestCase (), TestCase::QUICK);
+ AddTestCase (new Int64x64ArithmeticTestCase (), TestCase::QUICK);
+ AddTestCase (new Int64x64CompareTestCase (), TestCase::QUICK);
AddTestCase (new Int64x64InputTestCase (), TestCase::QUICK);
AddTestCase (new Int64x64InputOutputTestCase (), TestCase::QUICK);
- AddTestCase (new Int64x64ArithmeticTestCase (), TestCase::QUICK);
AddTestCase (new Int64x64Bug455TestCase (), TestCase::QUICK);
AddTestCase (new Int64x64Bug863TestCase (), TestCase::QUICK);
AddTestCase (new Int64x64Bug1786TestCase (), TestCase::QUICK);
- AddTestCase (new Int64x64CompareTestCase (), TestCase::QUICK);
AddTestCase (new Int64x64InvertTestCase (), TestCase::QUICK);
}
} g_int64x64TestSuite;