[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.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
#include <cmath>
|
||||
#include <cmath> // 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 */
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 @@ 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 */
|
||||
|
||||
@@ -2,18 +2,96 @@
|
||||
#if !defined(INT64X64_DOUBLE_H) && (defined (INT64X64_USE_DOUBLE) || defined(PYTHON_SCAN))
|
||||
#define INT64X64_DOUBLE_H
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <stdint.h>
|
||||
#include <cmath> // 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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <iostream>
|
||||
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user