Replace GncInt128’s flags variable with bit-stuffing in the high leg.

Loses three bits so GncInt128 becomes really a GncInt125, but we don’t
really need the single order-of-magnitude: 10**38 is big enough. Saves
a full word of memory for each GncInt128, which means 2 words for GncRational.
That’s a 33% saving in memory for 64-bit and makes the object size the
same (32 bytes) for all architectures.
This commit is contained in:
John Ralls 2017-01-31 14:21:00 -08:00
parent c633e80a24
commit 0403a6667a
3 changed files with 423 additions and 243 deletions

View File

@ -32,6 +32,7 @@ extern "C"
#include <utility>
#include <cassert>
#include <cstdio>
#include <sstream>
/* All algorithms from Donald E. Knuth, "The Art of Computer
* Programming, Volume 2: Seminumerical Algorithms", 3rd Ed.,
@ -42,13 +43,29 @@ namespace {
static const unsigned int sublegs = GncInt128::numlegs * 2;
static const unsigned int sublegbits = GncInt128::legbits / 2;
static const uint64_t sublegmask = (UINT64_C(1) << sublegbits) - 1;
static const uint64_t flagmask = UINT64_C(0xe000000000000000);
static const uint64_t nummask = UINT64_C(0x1fffffffffffffff);
/* Assumes that the high bits represent old flags to be replaced.
* Any overflow must be detected first!
*/
static inline uint64_t set_flags(uint64_t leg, uint8_t flags)
{
auto flag_part = static_cast<uint64_t>(flags) << 61;
return flag_part + (leg & nummask);
}
static inline uint8_t get_flags(uint64_t leg)
{
return (leg & flagmask) >> 61;
}
static inline uint64_t get_num(uint64_t leg)
{
return leg & nummask;
}
}
GncInt128::GncInt128 () : m_flags {}, m_hi {0}, m_lo {0}{}
GncInt128::GncInt128 () : m_hi {0}, m_lo {0}{}
GncInt128::GncInt128 (int64_t upper, int64_t lower, unsigned char flags) :
m_flags {static_cast<unsigned char>(flags ^ (upper < 0 ? neg :
upper == 0 && lower < 0 ? neg : pos))},
GncInt128::GncInt128 (int64_t upper, int64_t lower, uint8_t flags) :
m_hi {static_cast<uint64_t>(upper < 0 ? -upper : upper)},
m_lo {static_cast<uint64_t>(lower < 0 ? -lower : lower)}
{
@ -58,38 +75,75 @@ GncInt128::GncInt128 (int64_t upper, int64_t lower, unsigned char flags) :
m_lo += (m_hi << 63);
m_hi >>= 1;
if (m_hi & flagmask)
{
std::ostringstream ss;
ss << "Constructing GncInt128 with int64_t " << upper
<< " which is too big.";
throw std::overflow_error(ss.str());
}
flags ^= (upper < 0 ? neg : upper == 0 && lower < 0 ? neg : pos);
m_hi = set_flags(m_hi, flags);
}
GncInt128::GncInt128 (int64_t upper, uint64_t lower, unsigned char flags) :
m_flags {static_cast<unsigned char>(flags ^ (upper < 0 ? neg : pos))},
m_hi {static_cast<uint64_t>(upper < 0 ? -upper : upper)}, m_lo {lower} {}
GncInt128::GncInt128 (int64_t upper, uint64_t lower, uint8_t flags) :
m_hi {static_cast<uint64_t>(upper < 0 ? -upper : upper)}, m_lo {lower}
{
if (m_hi & flagmask)
{
std::ostringstream ss;
ss << "Constructing GncInt128 with int64_t " << upper
<< " which is too big when lower is unsigned.";
throw std::overflow_error(ss.str());
}
flags ^= (upper < 0 ? neg : pos);
m_hi = set_flags(m_hi, flags);
}
GncInt128::GncInt128 (uint64_t upper, uint64_t lower, unsigned char flags) :
m_flags {flags}, m_hi {upper}, m_lo {lower} {}
GncInt128::GncInt128 (uint64_t upper, uint64_t lower, uint8_t flags) :
m_hi {upper}, m_lo {lower}
{
/* somewhere in the bowels of gnc_module.scm compilation this gets called
* with upper=INT64_MAX, which would otherwise throw. Make it our max value
* instead.
*/
if (m_hi == UINT64_MAX)
m_hi = nummask;
if (m_hi & flagmask)
{
std::ostringstream ss;
ss << "Constructing GncInt128 with uint64_t " << upper
<< " which is too big.";
throw std::overflow_error(ss.str());
}
m_hi = set_flags(m_hi, flags);
}
GncInt128&
GncInt128::zero () noexcept
{
m_flags = 0;
m_lo = m_hi = UINT64_C(0);
return *this;
}
GncInt128::operator int64_t() const
{
if ((m_flags & neg) && isBig())
auto flags = get_flags(m_hi);
if ((flags & neg) && isBig())
throw std::underflow_error ("Negative value too large to represent as int64_t");
if ((m_flags & (overflow | NaN)) || isBig())
if ((flags & (overflow | NaN)) || isBig())
throw std::overflow_error ("Value too large to represent as int64_t");
int64_t retval = static_cast<int64_t>(m_lo);
return m_flags & neg ? -retval : retval;
return flags & neg ? -retval : retval;
}
GncInt128::operator uint64_t() const
{
if (m_flags & neg)
auto flags = get_flags(m_hi);
if (flags & neg)
throw std::underflow_error ("Can't represent negative value as uint64_t");
if ((m_flags & (overflow | NaN)) || (m_hi || m_lo > UINT64_MAX))
if ((flags & (overflow | NaN)) || (m_hi || m_lo > UINT64_MAX))
throw std::overflow_error ("Value to large to represent as uint64_t");
return m_lo;
}
@ -98,22 +152,25 @@ GncInt128::operator uint64_t() const
int
GncInt128::cmp (const GncInt128& b) const noexcept
{
if (m_flags & (overflow | NaN))
auto flags = get_flags(m_hi);
if (flags & (overflow | NaN))
return -1;
if (b.isOverflow () || b.isNan ())
return 1;
if (m_flags & neg)
auto hi = get_num(m_hi);
auto bhi = get_num(b.m_hi);
if (flags & neg)
{
if (!b.isNeg()) return -1;
if (m_hi > b.m_hi) return -1;
if (m_hi < b.m_hi) return 1;
if (hi > bhi) return -1;
if (hi < bhi) return 1;
if (m_lo > b.m_lo) return -1;
if (m_lo < b.m_lo) return 1;
return 0;
}
if (b.isNeg()) return 1;
if (m_hi < b.m_hi) return -1;
if (m_hi > b.m_hi) return 1;
if (hi < bhi) return -1;
if (hi > bhi) return 1;
if (m_lo < b.m_lo) return -1;
if (m_lo > b.m_lo) return 1;
return 0;
@ -190,37 +247,38 @@ GncInt128::pow(unsigned int b) const noexcept
bool
GncInt128::isNeg () const noexcept
{
return (m_flags & neg);
return (get_flags(m_hi) & neg);
}
bool
GncInt128::isBig () const noexcept
{
return (m_hi || m_lo > INT64_MAX);
return (get_num(m_hi) || m_lo > INT64_MAX);
}
bool
GncInt128::isOverflow () const noexcept
{
return (m_flags & overflow);
return (get_flags(m_hi) & overflow);
}
bool
GncInt128::isNan () const noexcept
{
return (m_flags & NaN);
return (get_flags(m_hi) & NaN);
}
bool
GncInt128::valid() const noexcept
{
return !(m_flags & (overflow | NaN));
return !(get_flags(m_hi) & (overflow | NaN));
}
bool
GncInt128::isZero() const noexcept
{
return ((m_flags & (overflow | NaN)) == 0 && m_hi == 0 && m_lo == 0);
return ((get_flags(m_hi) & (overflow | NaN)) == 0 &&
get_num(m_hi) == 0 && m_lo == 0);
}
GncInt128
@ -235,8 +293,9 @@ GncInt128::abs() const noexcept
unsigned int
GncInt128::bits() const noexcept
{
unsigned int bits {static_cast<unsigned int>(m_hi == 0 ? 0 : 64)};
uint64_t temp {(m_hi == 0 ? m_lo : m_hi)};
auto hi = get_num(m_hi);
unsigned int bits {static_cast<unsigned int>(hi == 0 ? 0 : 64)};
uint64_t temp {(hi == 0 ? m_lo : hi)};
for (;temp > 0; temp >>= 1)
++bits;
return bits;
@ -247,10 +306,12 @@ GncInt128
GncInt128::operator-() const noexcept
{
auto retval = *this;
auto flags = get_flags(retval.m_hi);
if (isNeg())
retval.m_flags ^= neg;
flags ^= neg;
else
retval.m_flags |= neg;
flags |= neg;
retval.m_hi = set_flags(retval.m_hi, flags);
return retval;
}
@ -286,11 +347,12 @@ GncInt128::operator-- (int) noexcept
GncInt128&
GncInt128::operator+= (const GncInt128& b) noexcept
{
auto flags = get_flags(m_hi);
if (b.isOverflow())
m_flags |= overflow;
flags |= overflow;
if (b.isNan())
m_flags |= NaN;
flags |= NaN;
m_hi = set_flags(m_hi, flags);
if (isOverflow() || isNan())
return *this;
if ((isNeg () && !b.isNeg ()) || (!isNeg () && b.isNeg ()))
@ -298,32 +360,38 @@ GncInt128::operator+= (const GncInt128& b) noexcept
uint64_t result = m_lo + b.m_lo;
uint64_t carry = static_cast<int64_t>(result < m_lo); //Wrapping
m_lo = result;
result = m_hi + b.m_hi + carry;
if (result < m_hi)
m_flags |= overflow;
m_hi = result;
auto hi = get_num(m_hi);
auto bhi = get_num(b.m_hi);
result = hi + bhi + carry;
if (result < hi || result & flagmask)
flags |= overflow;
m_hi = set_flags(result, flags);
return *this;
}
GncInt128&
GncInt128::operator<<= (unsigned int i) noexcept
{
auto flags = get_flags(m_hi);
if (i > maxbits)
{
m_flags &= 0xfe;
m_hi = 0;
flags &= 0xfe;
m_hi = set_flags(0, flags);
m_lo = 0;
return *this;
}
auto hi = get_num(m_hi);
if (i < legbits)
{
uint64_t carry {(m_lo & (((UINT64_C(1) << i) - 1) << (legbits - i)))};
m_lo <<= i;
m_hi <<= i;
m_hi += carry;
hi <<= i;
hi += carry;
m_hi = set_flags(hi, flags);
return *this;
}
m_hi = m_lo << (i - legbits);
hi = m_lo << (i - legbits);
m_hi = set_flags(hi, flags);
m_lo = 0;
return *this;
}
@ -331,33 +399,38 @@ GncInt128::operator<<= (unsigned int i) noexcept
GncInt128&
GncInt128::operator>>= (unsigned int i) noexcept
{
auto flags = get_flags(m_hi);
if (i > maxbits)
{
m_flags &= 0xfe;
m_hi = 0;
flags &= 0xfe;
m_hi = set_flags(0, flags);
m_lo = 0;
return *this;
}
auto hi = get_num(m_hi);
if (i < legbits)
{
uint64_t carry {(m_hi & ((UINT64_C(1) << i) - 1))};
uint64_t carry {(hi & ((UINT64_C(1) << i) - 1))};
m_lo >>= i;
m_hi >>= i;
hi >>= i;
m_lo += (carry << (legbits - i));
m_hi = set_flags(hi, flags);
return *this;
}
m_lo = m_hi >> (i - legbits);
m_hi = 0;
m_lo = hi >> (i - legbits);
m_hi = set_flags(0, flags);
return *this;
}
GncInt128&
GncInt128::operator-= (const GncInt128& b) noexcept
{
auto flags = get_flags(m_hi);
if (b.isOverflow())
m_flags |= overflow;
flags |= overflow;
if (b.isNan())
m_flags |= NaN;
flags |= NaN;
m_hi = set_flags(m_hi, flags);
if (isOverflow() || isNan())
return *this;
@ -365,10 +438,11 @@ GncInt128::operator-= (const GncInt128& b) noexcept
if ((!isNeg() && b.isNeg()) || (isNeg() && !b.isNeg()))
return this->operator+= (-b);
bool operand_bigger {abs().cmp (b.abs()) < 0};
auto hi = get_num(m_hi);
auto far_hi = get_num(b.m_hi);
if (operand_bigger)
{
m_flags ^= neg; // ^= flips the bit
uint64_t far_hi {b.m_hi};
flags ^= neg; // ^= flips the bit
if (m_lo > b.m_lo)
{
/* The + 1 on the end is because we really want to use 2^64, or
@ -379,20 +453,20 @@ GncInt128::operator-= (const GncInt128& b) noexcept
}
else
m_lo = b.m_lo - m_lo;
m_hi = far_hi - m_hi;
hi = far_hi - hi;
m_hi = set_flags(hi, flags);
return *this;
}
if (m_lo < b.m_lo)
{
m_lo = UINT64_MAX - b.m_lo + m_lo + 1; //See UINT64_MAX comment above
--m_hi; //borrow
--hi; //borrow
}
else
m_lo -= b.m_lo;
m_hi -= b.m_hi;
hi -= far_hi;
m_hi = set_flags(hi, flags);
return *this;
}
@ -400,38 +474,45 @@ GncInt128&
GncInt128::operator*= (const GncInt128& b) noexcept
{
/* Test for 0 first */
auto flags = get_flags(m_hi);
if (isZero() || b.isZero())
{
m_hi = m_lo = 0;
m_lo = 0;
m_hi = set_flags(0, flags);
return *this;
}
if (b.isOverflow())
m_flags |= overflow;
flags |= overflow;
if (b.isNan())
m_flags |= NaN;
flags |= NaN;
m_hi = set_flags(m_hi, flags);
if (isOverflow() || isNan())
return *this;
/* Test for overflow before spending time on the calculation */
if (m_hi && b.m_hi)
auto hi = get_num(m_hi);
auto bhi = get_num(b.m_hi);
if (hi && bhi)
{
m_flags |= overflow;
flags |= overflow;
m_hi = set_flags(hi, flags);
return *this;
}
unsigned int abits {bits()}, bbits {b.bits()};
if (abits + bbits > maxbits)
{
m_flags |= overflow;
flags |= overflow;
m_hi = set_flags(m_hi, flags);
return *this;
}
/* Handle the sign; ^ flips if b is negative */
m_flags ^= (b.m_flags & neg);
flags ^= (get_flags(b.m_hi) & neg);
/* The trivial case */
if (abits + bbits <= legbits)
{
m_lo *= b.m_lo;
m_hi = set_flags(m_hi, flags);
return *this;
}
@ -448,9 +529,9 @@ GncInt128::operator*= (const GncInt128& b) noexcept
*/
uint64_t av[sublegs] {(m_lo & sublegmask), (m_lo >> sublegbits),
(m_hi & sublegmask), (m_hi >> sublegbits)};
(hi & sublegmask), (hi >> sublegbits)};
uint64_t bv[sublegs] {(b.m_lo & sublegmask), (b.m_lo >> sublegbits),
(b.m_hi & sublegmask), (b.m_hi >> sublegbits)};
(bhi & sublegmask), (bhi >> sublegbits)};
uint64_t rv[sublegs] {};
uint64_t carry {}, scratch {};
@ -478,19 +559,23 @@ GncInt128::operator*= (const GncInt128& b) noexcept
if (carry) //Shouldn't happen because of the checks above
{
m_flags |= overflow;
flags |= overflow;
m_hi = set_flags(m_hi, flags);
return *this;
}
m_lo = rv[0] + (rv[1] << sublegbits);
carry = rv[1] >> sublegbits;
carry += (rv[1] << sublegbits) > m_lo || rv[0] > m_lo ? 1 : 0;
m_hi = rv[2] + (rv[3] << sublegbits) + carry;
if ((rv[3] << sublegbits) > m_hi || rv[2] > m_hi || (rv[3] >> sublegbits))
hi = rv[2] + (rv[3] << sublegbits) + carry;
if ((rv[3] << sublegbits) > hi || rv[2] > hi || (rv[3] >> sublegbits) ||
hi & flagmask)
{
m_flags |= overflow;
flags |= overflow;
m_hi = set_flags(hi, flags);
return *this;
}
m_hi = set_flags(hi, flags);
return *this;
}
@ -501,7 +586,8 @@ namespace {
*/
/* We're using arrays here instead of vectors to avoid an alloc. */
void
div_multi_leg (uint64_t* u, size_t m, uint64_t* v, size_t n, GncInt128& q, GncInt128& r) noexcept
div_multi_leg (uint64_t* u, size_t m, uint64_t* v, size_t n,
GncInt128& q, GncInt128& r) noexcept
{
/* D1, Normalization */
uint64_t qv[sublegs] {};
@ -598,7 +684,8 @@ div_multi_leg (uint64_t* u, size_t m, uint64_t* v, size_t n, GncInt128& q, GncIn
}
void
div_single_leg (uint64_t* u, size_t m, uint64_t v, GncInt128& q, GncInt128& r) noexcept
div_single_leg (uint64_t* u, size_t m, uint64_t v,
GncInt128& q, GncInt128& r) noexcept
{
uint64_t qv[sublegs] {};
uint64_t carry {};
@ -625,17 +712,23 @@ div_single_leg (uint64_t* u, size_t m, uint64_t v, GncInt128& q, GncInt128& r) n
void
GncInt128::div (const GncInt128& b, GncInt128& q, GncInt128& r) const noexcept
{
auto qflags = get_flags(q.m_hi);
auto rflags = get_flags(r.m_hi);
if (isOverflow() || b.isOverflow())
{
q.m_flags |= overflow;
r.m_flags |= overflow;
qflags |= overflow;
rflags |= overflow;
q.m_hi = set_flags(q.m_hi, qflags);
r.m_hi = set_flags(r.m_hi, rflags);
return;
}
if (isNan() || b.isNan())
{
q.m_flags |= NaN;
r.m_flags |= NaN;
qflags |= NaN;
rflags |= NaN;
q.m_hi = set_flags(q.m_hi, qflags);
r.m_hi = set_flags(r.m_hi, rflags);
return;
}
assert (&q != this);
@ -646,23 +739,28 @@ GncInt128::div (const GncInt128& b, GncInt128& q, GncInt128& r) const noexcept
q.zero(), r.zero();
if (b.isZero())
{
q.m_flags |= NaN;
r.m_flags |= NaN;
qflags |= NaN;
rflags |= NaN;
q.m_hi = set_flags(q.m_hi, qflags);
r.m_hi = set_flags(r.m_hi, rflags);
return;
}
if (isNeg())
q.m_flags |= neg;
qflags |= neg;
if (b.isNeg())
q.m_flags ^= neg;
qflags ^= neg;
if (abs() < b.abs())
{
r = *this;
return;
}
if (m_hi == 0 && b.m_hi == 0) //let the hardware do it
auto hi = get_num(m_hi);
auto bhi = get_num(b.m_hi);
q.m_hi = set_flags(hi, qflags);
if (hi == 0 && bhi == 0) //let the hardware do it
{
q.m_lo = m_lo / b.m_lo;
r.m_lo = m_lo % b.m_lo;
@ -670,9 +768,9 @@ GncInt128::div (const GncInt128& b, GncInt128& q, GncInt128& r) const noexcept
}
uint64_t u[sublegs + 2] {(m_lo & sublegmask), (m_lo >> sublegbits),
(m_hi & sublegmask), (m_hi >> sublegbits), 0, 0};
(hi & sublegmask), (hi >> sublegbits), 0, 0};
uint64_t v[sublegs] {(b.m_lo & sublegmask), (b.m_lo >> sublegbits),
(b.m_hi & sublegmask), (b.m_hi >> sublegbits)};
(bhi & sublegmask), (bhi >> sublegbits)};
auto m = u[3] ? 4 : u[2] ? 3 : u[1] ? 2 : u[0] ? 1 : 0;
auto n = v[3] ? 4 : v[2] ? 3 : v[1] ? 2 : v[0] ? 1 : 0;
if (m == 0 || n == 0) //Shouldn't happen
@ -699,30 +797,35 @@ GncInt128::operator%= (const GncInt128& b) noexcept
div(b, q, r);
std::swap (*this, r);
if (q.isNan())
m_flags |= NaN;
m_hi = set_flags(m_hi, (get_flags(m_hi) | NaN));
return *this;
}
GncInt128&
GncInt128::operator&= (const GncInt128& b) noexcept
{
auto flags = get_flags(m_hi);
if (b.isOverflow())
m_flags |= overflow;
flags |= overflow;
if (b.isNan())
m_flags |= NaN;
flags |= NaN;
m_hi = set_flags(m_hi, flags);
if (isOverflow() || isNan())
return *this;
m_hi &= b.m_hi;
auto hi = get_num(m_hi);
hi &= get_num(b.m_hi);
m_lo &= b.m_lo;
m_hi = set_flags(hi, flags);
return *this;
}
GncInt128&
GncInt128::operator|= (const GncInt128& b) noexcept
{
m_hi ^= b.m_hi;
auto flags = get_flags(m_hi);
auto hi = get_num(m_hi);
hi ^= get_num(b.m_hi);
m_hi = set_flags(hi, flags);
m_lo ^= b.m_lo;
return *this;
}
@ -730,15 +833,17 @@ GncInt128::operator|= (const GncInt128& b) noexcept
GncInt128&
GncInt128::operator^= (const GncInt128& b) noexcept
{
auto flags = get_flags(m_hi);
if (b.isOverflow())
m_flags |= overflow;
flags |= overflow;
if (b.isNan())
m_flags |= NaN;
flags |= NaN;
m_hi = set_flags(m_hi, flags);
if (isOverflow() || isNan())
return *this;
m_hi ^= b.m_hi;
auto hi = get_num(m_hi);
hi ^= get_num(b.m_hi);
m_hi = set_flags(hi, flags);
m_lo ^= b.m_lo;
return *this;
}
@ -801,7 +906,7 @@ GncInt128::asCharBufR(char* buf) const noexcept
return buf;
}
uint64_t d[dec_array_size] {};
decimal_from_binary(d, m_hi, m_lo);
decimal_from_binary(d, get_num(m_hi), m_lo);
char* next = buf;
char neg {'-'};

View File

@ -48,19 +48,21 @@ extern "C"
/** @addtogroup GncInt128
* @ingroup QOF
* @{
* @brief provides a 128-bit int as a base class for GncNumeric.
* @brief provides a 125-bit int as a base class for GncNumeric.
*
* All the usual operators are provided. Only the explicit integer
* conversions throw; all other errors are indicated by the overflow
* and NaN ("Not a Number") flags. Note that performing any operation
* on an overflowed or NaN Gncint128 will yield an overflowed or NaN
* result, so calling routines need not check until the end of a
* chained calculation.
* In order to make space for the status flags the upper leg is limited to
* 0x1fffffffffffffff. Attempting to construct a GncInt128 with a larger upper
* leg will throw a std::overflow_error.
*
* All the usual operators are provided. Only the constructors and explicit
* integer conversions throw; all other errors are indicated by the overflow and
* NaN ("Not a Number") flags. Note that performing any operation on an
* overflowed or NaN Gncint128 will yield an overflowed or NaN result, so
* calling routines need not check until the end of a chained calculation.
* GncInt128 uses implicit copy and move constructors and implicit destructor.
*/
class GncInt128
{
unsigned char m_flags;
uint64_t m_hi;
uint64_t m_lo;

View File

@ -24,6 +24,8 @@
#include <gtest/gtest.h>
#include "../gnc-int128.hpp"
static constexpr uint64_t UPPER_MAX{2305843009213693951};
TEST(qofint128_constructors, test_default_constructor)
{
GncInt128 value {};
@ -36,112 +38,146 @@ TEST(qofint128_constructors, test_default_constructor)
TEST(qofint128_constructors, test_single_arg_constructor)
{
GncInt128 value1 (INT64_C(0));
EXPECT_TRUE (value1.isZero());
EXPECT_FALSE (value1.isNeg());
EXPECT_FALSE (value1.isBig());
EXPECT_FALSE (value1.isOverflow());
EXPECT_FALSE (value1.isNan());
EXPECT_NO_THROW({
GncInt128 value1 (INT64_C(0));
EXPECT_TRUE (value1.isZero());
EXPECT_FALSE (value1.isNeg());
EXPECT_FALSE (value1.isBig());
EXPECT_FALSE (value1.isOverflow());
EXPECT_FALSE (value1.isNan());
});
GncInt128 value2 (INT64_C(567894392130486208));
EXPECT_FALSE (value2.isZero());
EXPECT_FALSE (value2.isNeg());
EXPECT_FALSE (value2.isBig());
EXPECT_FALSE (value2.isOverflow());
EXPECT_FALSE (value2.isNan());
EXPECT_NO_THROW({
GncInt128 value2 (INT64_C(567894392130486208));
EXPECT_FALSE (value2.isZero());
EXPECT_FALSE (value2.isNeg());
EXPECT_FALSE (value2.isBig());
EXPECT_FALSE (value2.isOverflow());
EXPECT_FALSE (value2.isNan());
});
GncInt128 value3 (INT64_C(-567894392130486208));
EXPECT_FALSE (value3.isZero());
EXPECT_TRUE (value3.isNeg());
EXPECT_FALSE (value3.isBig());
EXPECT_FALSE (value3.isOverflow());
EXPECT_FALSE (value3.isNan());
EXPECT_NO_THROW({
GncInt128 value3 (INT64_C(-567894392130486208));
EXPECT_FALSE (value3.isZero());
EXPECT_TRUE (value3.isNeg());
EXPECT_FALSE (value3.isBig());
EXPECT_FALSE (value3.isOverflow());
EXPECT_FALSE (value3.isNan());
});
GncInt128 value4 (UINT64_C(13567894392130486208));
EXPECT_FALSE (value4.isZero());
EXPECT_FALSE (value4.isNeg());
EXPECT_TRUE (value4.isBig());
EXPECT_FALSE (value4.isOverflow());
EXPECT_FALSE (value4.isNan());
EXPECT_NO_THROW({
GncInt128 value4 (UINT64_C(13567894392130486208));
EXPECT_FALSE (value4.isZero());
EXPECT_FALSE (value4.isNeg());
EXPECT_TRUE (value4.isBig());
EXPECT_FALSE (value4.isOverflow());
EXPECT_FALSE (value4.isNan());
});
}
TEST(qofint128_constructors, test_double_arg_constructor)
{
GncInt128 value1 (INT64_C(0), INT64_C(0));
EXPECT_TRUE (value1.isZero());
EXPECT_FALSE (value1.isNeg());
EXPECT_FALSE (value1.isBig());
EXPECT_FALSE (value1.isOverflow());
EXPECT_FALSE (value1.isNan());
EXPECT_NO_THROW({
GncInt128 value1 (INT64_C(0), INT64_C(0));
EXPECT_TRUE (value1.isZero());
EXPECT_FALSE (value1.isNeg());
EXPECT_FALSE (value1.isBig());
EXPECT_FALSE (value1.isOverflow());
EXPECT_FALSE (value1.isNan());
});
GncInt128 value2 (INT64_C(0), INT64_C(567894392130486208));
EXPECT_FALSE (value2.isZero());
EXPECT_FALSE (value2.isNeg());
EXPECT_FALSE (value2.isBig());
EXPECT_FALSE (value2.isOverflow());
EXPECT_FALSE (value2.isNan());
EXPECT_NO_THROW({
GncInt128 value2 (INT64_C(0), INT64_C(567894392130486208));
EXPECT_FALSE (value2.isZero());
EXPECT_FALSE (value2.isNeg());
EXPECT_FALSE (value2.isBig());
EXPECT_FALSE (value2.isOverflow());
EXPECT_FALSE (value2.isNan());
});
GncInt128 value3 (INT64_C(567894392130486208), INT64_C(0));
EXPECT_FALSE (value3.isZero());
EXPECT_FALSE (value3.isNeg());
EXPECT_TRUE (value3.isBig());
EXPECT_FALSE (value3.isOverflow());
EXPECT_FALSE (value3.isNan());
EXPECT_NO_THROW({
GncInt128 value3 (INT64_C(567894392130486208), INT64_C(0));
EXPECT_FALSE (value3.isZero());
EXPECT_FALSE (value3.isNeg());
EXPECT_TRUE (value3.isBig());
EXPECT_FALSE (value3.isOverflow());
EXPECT_FALSE (value3.isNan());
});
GncInt128 value4 (INT64_C(567894392130486208), INT64_C(567894392130486208));
EXPECT_FALSE (value4.isZero());
EXPECT_FALSE (value4.isNeg());
EXPECT_TRUE (value4.isBig());
EXPECT_FALSE (value4.isOverflow());
EXPECT_FALSE (value4.isNan());
EXPECT_NO_THROW({
GncInt128 value4 (INT64_C(567894392130486208),
INT64_C(567894392130486208));
EXPECT_FALSE (value4.isZero());
EXPECT_FALSE (value4.isNeg());
EXPECT_TRUE (value4.isBig());
EXPECT_FALSE (value4.isOverflow());
EXPECT_FALSE (value4.isNan());
});
GncInt128 value5 (INT64_C(567894392130486208),
INT64_C(-567894392130486208));
EXPECT_FALSE (value5.isZero());
EXPECT_FALSE (value5.isNeg());
EXPECT_TRUE (value5.isBig());
EXPECT_FALSE (value5.isOverflow());
EXPECT_FALSE (value5.isNan());
EXPECT_NO_THROW({
GncInt128 value5 (INT64_C(567894392130486208),
INT64_C(-567894392130486208));
EXPECT_FALSE (value5.isZero());
EXPECT_FALSE (value5.isNeg());
EXPECT_TRUE (value5.isBig());
EXPECT_FALSE (value5.isOverflow());
EXPECT_FALSE (value5.isNan());
});
GncInt128 value6 (INT64_C(-567894392130486208),
INT64_C(567894392130486208));
EXPECT_FALSE (value6.isZero());
EXPECT_TRUE (value6.isNeg());
EXPECT_TRUE (value6.isBig());
EXPECT_FALSE (value6.isOverflow());
EXPECT_FALSE (value6.isNan());
EXPECT_NO_THROW({
GncInt128 value6 (INT64_C(-567894392130486208),
INT64_C(567894392130486208));
EXPECT_FALSE (value6.isZero());
EXPECT_TRUE (value6.isNeg());
EXPECT_TRUE (value6.isBig());
EXPECT_FALSE (value6.isOverflow());
EXPECT_FALSE (value6.isNan());
});
GncInt128 value7 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), GncInt128::pos);
EXPECT_FALSE (value7.isZero());
EXPECT_FALSE (value7.isNeg());
EXPECT_TRUE (value7.isBig());
EXPECT_FALSE (value7.isOverflow());
EXPECT_FALSE (value7.isNan());
EXPECT_NO_THROW({
GncInt128 value7 (UINT64_C(1695986799016310843),
UINT64_C(13567894392130486208), GncInt128::pos);
EXPECT_FALSE (value7.isZero());
EXPECT_FALSE (value7.isNeg());
EXPECT_TRUE (value7.isBig());
EXPECT_FALSE (value7.isOverflow());
EXPECT_FALSE (value7.isNan());
});
GncInt128 value8 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), GncInt128::neg);
EXPECT_FALSE (value8.isZero());
EXPECT_TRUE (value8.isNeg());
EXPECT_TRUE (value8.isBig());
EXPECT_FALSE (value8.isOverflow());
EXPECT_FALSE (value8.isNan());
EXPECT_NO_THROW({
GncInt128 value8 (UINT64_C(1695986799016310843),
UINT64_C(13567894392130486208), GncInt128::neg);
EXPECT_FALSE (value8.isZero());
EXPECT_TRUE (value8.isNeg());
EXPECT_TRUE (value8.isBig());
EXPECT_FALSE (value8.isOverflow());
EXPECT_FALSE (value8.isNan());
});
GncInt128 value9 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), GncInt128::overflow);
EXPECT_FALSE (value9.isZero());
EXPECT_FALSE (value9.isNeg());
EXPECT_TRUE (value9.isBig());
EXPECT_TRUE (value9.isOverflow());
EXPECT_FALSE (value9.isNan());
EXPECT_NO_THROW({
GncInt128 value9 (UINT64_C(1695986799016310843),
UINT64_C(13567894392130486208),
GncInt128::overflow);
EXPECT_FALSE (value9.isZero());
EXPECT_FALSE (value9.isNeg());
EXPECT_TRUE (value9.isBig());
EXPECT_TRUE (value9.isOverflow());
EXPECT_FALSE (value9.isNan());
});
GncInt128 value10 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), GncInt128::NaN);
EXPECT_FALSE (value10.isZero());
EXPECT_FALSE (value10.isNeg());
EXPECT_TRUE (value10.isBig());
EXPECT_FALSE (value10.isOverflow());
EXPECT_TRUE (value10.isNan());
EXPECT_NO_THROW({
GncInt128 value10 (UINT64_C(1695986799016310843),
UINT64_C(13567894392130486208), GncInt128::NaN);
EXPECT_FALSE (value10.isZero());
EXPECT_FALSE (value10.isNeg());
EXPECT_TRUE (value10.isBig());
EXPECT_FALSE (value10.isOverflow());
EXPECT_TRUE (value10.isNan());
});
EXPECT_THROW(GncInt128 value10 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208)),
std::overflow_error);
}
TEST(qofint128_functions, test_int_functions)
@ -149,9 +185,13 @@ TEST(qofint128_functions, test_int_functions)
int64_t arg {INT64_C(567894392130486208)};
int64_t narg {INT64_C(-567894392130486208)};
uint64_t uarg {UINT64_C(13567894392130486208)};
GncInt128 value1 (INT64_C(0), arg);
EXPECT_EQ (arg, static_cast<int64_t>(value1));
EXPECT_EQ (static_cast<uint64_t>(arg), static_cast<uint64_t>(value1));
EXPECT_NO_THROW({
GncInt128 value1 (INT64_C(0), arg);
EXPECT_EQ (arg, static_cast<int64_t>(value1));
EXPECT_EQ (static_cast<uint64_t>(arg),
static_cast<uint64_t>(value1));
});
GncInt128 value2 (UINT64_C(0), uarg);
EXPECT_THROW (auto v = static_cast<int64_t>(value2), std::overflow_error);
@ -291,19 +331,22 @@ TEST(qofint128_functions, add_and_subtract)
{
/* UINT64_MAX = 18,446,744,073,709,551,615
* INT64_MAX = 9,223,372,036,854,775,807
* barg + sarg = INT64_MAX
* upper leg max = 2,305,843,009,213,693,951
* barg + marg = INT64_MAX
* barg + uarg = UINT64_MAX
* marg + sarg = upper leg max
*/
int64_t barg {INT64_C(4878849681579065407)};
int64_t sarg {INT64_C(4344522355275710400)};
int64_t marg {INT64_C(4344522355275710400)};
int64_t sarg {INT64_C(267163663151677502)};
uint64_t uarg {UINT64_C(13567894392130486208)};
GncInt128 smallest (sarg + 100);
GncInt128 smallest (marg + 100);
GncInt128 smaller (barg + 500);
GncInt128 small (uarg);
GncInt128 big (sarg, barg);
GncInt128 bigger (static_cast<uint64_t>(barg), uarg);
GncInt128 biggest (uarg, static_cast<uint64_t>(barg));
GncInt128 bigger (marg, barg);
GncInt128 biggest (sarg, static_cast<uint64_t>(barg) + uarg);
GncInt128 nsmall (UINT64_C(0), uarg, GncInt128::neg);
EXPECT_EQ (GncInt128(INT64_C(2), INT64_C(499)), small += smaller);
@ -311,12 +354,11 @@ TEST(qofint128_functions, add_and_subtract)
nsmall -= smaller);
EXPECT_EQ (GncInt128(uarg), small -= smaller);
EXPECT_EQ (GncInt128(static_cast<uint64_t>(barg + sarg/2), UINT64_MAX),
EXPECT_EQ (GncInt128(UINT64_C(2305843009213693951),
UINT64_C(9757699363158130814)),
bigger += big);
EXPECT_EQ (GncInt128(static_cast<uint64_t>(barg), uarg), bigger -= big);
EXPECT_EQ (GncInt128(marg, barg), bigger -= big);
bigger += biggest;
EXPECT_EQ (GncInt128(UINT64_MAX, UINT64_MAX), bigger);
bigger += smallest;
EXPECT_TRUE (bigger.isOverflow());
bigger -= biggest;
EXPECT_TRUE (bigger.isOverflow());
@ -325,38 +367,70 @@ TEST(qofint128_functions, add_and_subtract)
TEST(qofint128_functions, multiply)
{
int64_t barg {INT64_C(4878849681579065407)};
int64_t sarg {INT64_C(4344522355275710400)};
int64_t marg {INT64_C(4344522355275710400)};
int64_t sarg {INT64_C(267163663151677502)};
uint64_t uarg {UINT64_C(13567894392130486208)};
GncInt128 smallest (sarg);
GncInt128 smallest (marg);
GncInt128 smaller (barg);
GncInt128 small (uarg);
GncInt128 big (sarg, barg);
GncInt128 bigger (static_cast<uint64_t>(barg), uarg);
GncInt128 bigger (sarg, uarg);
GncInt128 nsmaller (-barg);
GncInt128 nsmallest (-marg);
GncInt128 tiny (53);
GncInt128 teensy (37);
small *= big;
EXPECT_TRUE (small.isOverflow());
big *= bigger;
EXPECT_TRUE (big.isOverflow());
EXPECT_EQ (GncInt128(UINT64_C(1149052180967758316), UINT64_C(6323251814974894144)), smallest *= smaller);
EXPECT_FALSE (smallest.isOverflow());
EXPECT_NO_THROW({
small *= big;
EXPECT_TRUE (small.isOverflow());
big *= bigger;
EXPECT_TRUE (big.isOverflow());
EXPECT_EQ(1961, tiny * teensy);
EXPECT_EQ(-1961, -tiny * teensy);
EXPECT_EQ(-1961, tiny * -teensy);
EXPECT_EQ(1961, -tiny * -teensy);
EXPECT_EQ (GncInt128(INT64_C(1149052180967758316),
UINT64_C(6323251814974894144)),
smallest * smaller);
EXPECT_EQ (GncInt128(INT64_C(-1149052180967758316),
UINT64_C(6323251814974894144)),
-smallest * smaller);
EXPECT_EQ (GncInt128(INT64_C(-1149052180967758316),
UINT64_C(6323251814974894144)),
smallest * -smaller);
EXPECT_EQ (GncInt128(INT64_C(1149052180967758316),
UINT64_C(6323251814974894144)),
-smallest * -smaller);
EXPECT_EQ (GncInt128(INT64_C(-1149052180967758316),
UINT64_C(6323251814974894144)),
nsmallest * smaller);
EXPECT_EQ (GncInt128(INT64_C(-1149052180967758316),
UINT64_C(6323251814974894144)),
smallest * nsmaller);
EXPECT_EQ (GncInt128(INT64_C(1149052180967758316),
UINT64_C(6323251814974894144)),
nsmallest * nsmaller);
EXPECT_FALSE (smallest.isOverflow());
});
}
TEST(qofint128_functions, divide)
{
int64_t barg {INT64_C(4878849681579065407)};
int64_t sarg {INT64_C(4344522355275710400)};
int64_t marg {INT64_C(4344522355275710400)};
int64_t sarg {INT64_C(267163663151677502)};
uint64_t uarg {UINT64_C(13567894392130486208)};
GncInt128 zero (INT64_C(0));
GncInt128 one (INT64_C(1));
GncInt128 two (INT64_C(2));
GncInt128 smallest (sarg);
GncInt128 smallest (marg);
GncInt128 smaller (barg);
GncInt128 small (uarg);
GncInt128 big (sarg, barg);
GncInt128 bigger (static_cast<uint64_t>(barg), uarg);
GncInt128 bigger (static_cast<uint64_t>(sarg), uarg);
GncInt128 nsmall = -small;
GncInt128 nbig = -bigger;
@ -392,28 +466,24 @@ TEST(qofint128_functions, divide)
EXPECT_EQ (zero, r);
big.div (smaller, q, r);
EXPECT_EQ (GncInt128(INT64_C(8213236443097627766)), q);
EXPECT_EQ (GncInt128(INT64_C(3162679692459777845)), r);
EXPECT_EQ (GncInt128(INT64_C(505067796878574019)), q);
EXPECT_EQ (GncInt128(INT64_C(1659575984014676290)), r);
bigger.div (big, q, r);
EXPECT_EQ (two, q);
EXPECT_EQ (GncInt128(UINT64_C(534327326303355007),
UINT64_C(3810195028972355394)), r);
EXPECT_EQ (GncInt128(UINT64_C(3810195028972355394)), r);
bigger.div (-big, q, r);
EXPECT_EQ (-two, q);
EXPECT_EQ (GncInt128(UINT64_C(534327326303355007),
UINT64_C(3810195028972355394)), r);
EXPECT_EQ (GncInt128(UINT64_C(3810195028972355394)), r);
nbig.div (-big, q, r);
EXPECT_EQ (two, q);
EXPECT_EQ (GncInt128(UINT64_C(534327326303355007),
UINT64_C(3810195028972355394)), r);
EXPECT_EQ (GncInt128(UINT64_C(3810195028972355394)), r);
nbig.div (-big, q, r);
EXPECT_EQ (two, q);
EXPECT_EQ (GncInt128(UINT64_C(534327326303355007),
UINT64_C(3810195028972355394)), r);
EXPECT_EQ (GncInt128(UINT64_C(3810195028972355394)), r);
big.div (bigger, q, r);
EXPECT_EQ (zero, q);
@ -422,7 +492,7 @@ TEST(qofint128_functions, divide)
big.div (big - 1, q, r);
EXPECT_EQ(one, q);
EXPECT_EQ(one, r);
EXPECT_EQ (big, big %= bigger);
EXPECT_EQ (two, bigger /= big);
}
@ -430,24 +500,27 @@ TEST(qofint128_functions, divide)
TEST(qofint128_functions, GCD)
{
int64_t barg {INT64_C(4878849681579065407)};
int64_t sarg {INT64_C(4344522355275710401)};
int64_t marg {INT64_C(4344522355275710400)};
int64_t sarg {INT64_C(267163663151677502)};
uint64_t uarg {UINT64_C(13567894392130486208)};
GncInt128 one (INT64_C(1));
GncInt128 smallest (sarg);
GncInt128 smaller (barg);
GncInt128 small (uarg);
GncInt128 smaller (marg);
GncInt128 small (barg);
GncInt128 big = smaller * smallest;
GncInt128 bigger = small * smaller;
EXPECT_NO_THROW({
GncInt128 big = smaller * smallest;
GncInt128 bigger = small * smaller;
EXPECT_EQ (smaller, big.gcd(smaller));
EXPECT_EQ (smallest, big.gcd(smallest));
EXPECT_EQ (small, bigger.gcd(small));
EXPECT_EQ (smaller, bigger.gcd(smaller));
EXPECT_EQ (one, big.gcd (small));
EXPECT_EQ (one, bigger.gcd (smallest));
EXPECT_EQ (big, smaller.lcm (smallest));
EXPECT_EQ (smaller, big.gcd(smaller));
EXPECT_EQ (smallest, big.gcd(smallest));
EXPECT_EQ (small, bigger.gcd(small));
EXPECT_EQ (smaller, bigger.gcd(smaller));
EXPECT_EQ (one, big.gcd (small));
EXPECT_EQ (2, bigger.gcd (smallest));
EXPECT_EQ (big >> 1, smaller.lcm (smallest));
});
}
TEST(qofint128_functions, pow)