Begin QofInt128 class.

Constructs, adds & subtracts, prints, and compares.

Includes unit tests.
This commit is contained in:
John Ralls 2014-10-29 18:32:47 -07:00
parent 9d029d7f82
commit d4fdd9ef17
4 changed files with 878 additions and 0 deletions

View File

@ -0,0 +1,349 @@
/********************************************************************
* qofmath128.c -- an 128-bit integer library *
* Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
* Copyright (C) 2014 John Ralls <jralls@ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
*******************************************************************/
extern "C"
{
#include "config.h"
#include <inttypes.h>
}
#include "qofint128.hpp"
#include <iomanip>
/* All algorithms from Donald E. Knuth, "The Art of Computer
* Programming, Volume 2: Seminumerical Algorithms", 3rd Ed.,
* Addison-Wesley, 1998.
*/
QofInt128::QofInt128 () : m_flags {}, m_hi {0}, m_lo {0}{}
QofInt128::QofInt128 (int64_t lower) :
m_flags {static_cast<unsigned char>(lower < 0 ? neg : pos)},
m_hi {0},
m_lo {static_cast<uint64_t>(lower < 0 ? -lower : lower)} {}
QofInt128::QofInt128 (uint64_t lower) :
m_flags {}, m_hi {0}, m_lo {lower} {}
QofInt128::QofInt128 (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))},
m_hi {static_cast<uint64_t>(upper < 0 ? -upper : upper)},
m_lo {static_cast<uint64_t>(lower < 0 ? -lower : lower)}
{
if ((upper < 0 && lower > 0) || (upper > 0 && lower < 0))
m_lo = (m_hi << 63) - m_lo;
else
m_lo += (m_hi << 63);
m_hi >>= 1;
}
QofInt128::QofInt128 (uint64_t upper, uint64_t lower, unsigned char flags) :
m_flags {flags}, m_hi {upper},
m_lo {lower} {}
QofInt128::operator int64_t() const
{
if ((m_flags & neg) && isBig())
throw std::underflow_error ("Negative value to large to represent as int64_t");
if ((m_flags & (overflow | NaN)) || isBig())
throw std::overflow_error ("Value to large to represent as int64_t");
int64_t retval = static_cast<int64_t>(m_lo);
return m_flags & neg ? -retval : retval;
}
QofInt128::operator uint64_t() const
{
if (m_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))
throw std::overflow_error ("Value to large to represent as uint64_t");
return m_lo;
}
int
QofInt128::cmp (const QofInt128& b) const noexcept
{
if (m_flags & (overflow | NaN))
return -9;
if (b.isOverflow () || b.isNan ())
return 9;
if (m_flags & neg)
{
if (!b.isNeg()) return -1;
if (m_hi > b.m_hi) return -3;
if (m_hi < b.m_hi) return 3;
if (m_lo > b.m_lo) return -2;
if (m_lo < b.m_lo) return 2;
return 0;
}
if (b.isNeg()) return 1;
if (m_hi < b.m_hi) return -5;
if (m_hi > b.m_hi) return 5;
if (m_lo < b.m_lo) return -4;
if (m_lo > b.m_lo) return 4;
return 0;
}
bool
QofInt128::isNeg () const noexcept
{
return (m_flags & neg);
}
bool
QofInt128::isBig () const noexcept
{
return (m_hi || m_lo > INT64_MAX);
}
bool
QofInt128::isOverflow () const noexcept
{
return (m_flags & overflow);
}
bool
QofInt128::isNan () const noexcept
{
return (m_flags & NaN);
}
bool
QofInt128::isZero() const noexcept
{
return ((m_flags & (overflow | NaN)) == 0 && m_hi == 0 && m_lo == 0);
}
QofInt128
QofInt128::operator-() const noexcept
{
auto retval = *this;
if (isNeg())
retval.m_flags ^= neg;
else
retval.m_flags |= neg;
return retval;
}
QofInt128::operator bool() const noexcept
{
return ! isZero ();
}
QofInt128&
QofInt128::operator+= (const QofInt128& b) noexcept
{
if ((isNeg () && !b.isNeg ()) || (!isNeg () && b.isNeg ()))
return this->operator-= (-b);
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;
return *this;
}
QofInt128&
QofInt128::operator-= (const QofInt128& b) noexcept
{
if ((!isNeg() && b.isNeg()) || (isNeg() && !b.isNeg()))
return this->operator+= (-b);
bool operand_bigger {cmp (b) < 0};
if (operand_bigger)
{
m_flags ^= neg; // ^= flips the bit
uint64_t far_hi {b.m_hi};
if (m_lo > b.m_lo)
{
/* The + 1 on the end is because we really want to use 2^64, or
* UINT64_MAX + 1, but that can't be represented in a uint64_t.
*/
m_lo = UINT64_MAX - m_lo + b.m_lo + 1;
--far_hi; //borrow
}
else
m_lo = b.m_lo - m_lo;
m_hi = far_hi - m_hi;
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
}
else
m_lo -= b.m_lo;
m_hi -= b.m_hi;
return *this;
}
QofInt128&
QofInt128::operator*= (const QofInt128& b) noexcept
{
return *this;
}
QofInt128&
QofInt128::operator/= (const QofInt128& b) noexcept
{
return *this;
}
QofInt128&
QofInt128::operator%= (const QofInt128& b) noexcept
{
return *this;
}
static const uint8_t dec_array_size {5};
/* Convert a uint128 represented as a binary number into 4 10-digit decimal
* equivalents. Adapted from Douglas W. Jones, "Binary to Decimal Conversion in
* Limited Precision", http://homepage.cs.uiowa.edu/~jones/bcd/decimal.html,
* accessed 28 Oct 2014. */
static void
decimal_from_binary (uint64_t d[dec_array_size], uint64_t hi, uint64_t lo)
{
/* Coefficients are the values of 2^96, 2^64, and 2^32 divided into 8-digit
* segments:
* 2^96 = 79228,16251426,43375935,43950336
* 2^64 = 1844,67440737,09551616
* 2^32 = 42,94967296
*/
const uint8_t coeff_array_size = dec_array_size - 1;
const uint32_t coeff_3 [coeff_array_size] {79228, 16251426, 43375935, 43950336};
const uint32_t coeff_2 [coeff_array_size] {0, 1844, 67440737, 9551616};
const uint32_t coeff_1 [coeff_array_size] {0, 0, 42, 94967296};
const uint32_t bin_mask {0xffffffff};
const uint32_t dec_div {UINT32_C(100000000)};
const uint8_t last {dec_array_size - 1};
d[0] = lo & bin_mask;
d[1] = (lo >> 32) & bin_mask;
d[2] = hi & bin_mask;
d[3] = (hi >> 32) & bin_mask, 0;
d[0] += coeff_3[3] * d[3] + coeff_2[3] * d[2] + coeff_1[3] * d[1];
uint64_t q {d[0] / dec_div};
d[0] %= dec_div;
for (int i {1}; i < coeff_array_size; ++i)
{
int j = coeff_array_size - i - 1;
d[i] = q + coeff_3[j] * d[3] + coeff_2[j] * d[2] + coeff_1[j] * d[1];
q = d[i] / dec_div;
d[i] %= dec_div;
}
d[last] = q;
return;
}
static const uint8_t char_buf_size {41}; //39 digits plus sign and trailing null
char*
QofInt128::asCharBufR(char* buf) const noexcept
{
if (isOverflow())
{
sprintf (buf, "%s", "Overflow");
return buf;
}
if (isNan())
{
sprintf (buf, "%s", "NaN");
return buf;
}
uint64_t d[dec_array_size] {};
decimal_from_binary(d, m_hi, m_lo);
char* next = buf;
char neg {'-'};
if (isNeg()) *(next++) = neg;
bool trailing {false};
for (uint i {dec_array_size}; i; --i)
if (d[i - 1] || trailing)
{
if (trailing)
next += sprintf (next, "%8.8" PRIu64, d[i - 1]);
else
next += sprintf (next, "%" PRIu64, d[i - 1]);
trailing = true;
}
return buf;
}
std::ostream&
operator<< (std::ostream& stream, const QofInt128& a) noexcept
{
char buf[char_buf_size] {};
stream << a.asCharBufR (buf);
return stream;
}
bool
operator== (const QofInt128& a, const QofInt128& b) noexcept
{
return a.cmp(b) == 0;
}
bool
operator!= (const QofInt128& a, const QofInt128& b) noexcept
{
return a.cmp(b) != 0;
}
bool
operator< (const QofInt128& a, const QofInt128& b) noexcept
{
return a.cmp(b) < 0;
}
bool
operator> (const QofInt128& a, const QofInt128& b) noexcept
{
return a.cmp(b) > 0;
}
bool
operator<= (const QofInt128& a, const QofInt128& b) noexcept
{
return a.cmp(b) <= 0;
}
bool
operator>= (const QofInt128& a, const QofInt128& b) noexcept
{
return a.cmp(b) >= 0;
}

View File

@ -0,0 +1,193 @@
/********************************************************************
* qofmath128.h -- an 128-bit integer library *
* Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
* Copyright (C) 2014 John Ralls <jralls@ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
*******************************************************************/
#ifndef QOFINT128_H
#define QOFINT128_H
#include <stdint.h>
#include <stdexcept>
#include <string>
#include <ostream>
//using std::string;
/**
* @brief provides a 128-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 QofInt128 will yield an overflowed or NaN
* result, so calling routines need not check until the end of a
* chained calculation.
*/
class QofInt128
{
unsigned char m_flags;
uint64_t m_hi;
uint64_t m_lo;
public:
enum // Values for m_flags
{
pos = 0,
neg = 1,
overflow = 2,
NaN = 4
};
QofInt128 ();
QofInt128 (int64_t lower);
QofInt128 (uint64_t lower);
/**
* Construct a QofInt128 from two int64_t.
*
* N.B.: If the two parameters are of differing sign, it's taken to
* mean that the lower magnitude is *reducing* the magnitude of the
* upper, so the lower magnitude will be subracted from UINT64_MAX to
* obtain the lower limb value.
*/
QofInt128 (int64_t upper, int64_t lower, unsigned char flags = '\0');
QofInt128 (uint64_t upper, uint64_t lower, unsigned char flags = '\0');
/**
* Compare function.
*
* @return -1 if the object is less than the parameter, 0 if they're
* equal, and 1 if the object is greater.
*/
int cmp (const QofInt128& b) const noexcept;
/**
* Computes the Greatest Common Divisor between the object and paramter
*
* @return A QofInt128 having the GCD.
*/
QofInt128 gcd (const QofInt128& b) const noexcept;
/**
* Computes the Least Common Multiple between the object and paramter
*
* @return A QofInt128 having the LCM.
*/
QofInt128 lcm (const QofInt128& b) const noexcept;
/**
* Computes the object raised to the parameter's power
*
* @return A QofInt128; it will be NaN if the parameter is negative.
*/
QofInt128 pow (const QofInt128& b) const noexcept;
/**
* Explicit conversion to int64_t.
*
* @return A int64_t
* @throws std::overflow_error if the object's value is > INT64_MAX or NaN.
* @throws std::underflow_error if the object's value is < INT64_MIN
*/
explicit operator int64_t() const;
/**
* Explicit conversion to uint64_t.
*
* @return A uint64_t
* @throws std::overflow_error if the object's value is > UINT64_MAX or NaN.
* @throws std::underflow_error if the object's value is < 0.
*/
explicit operator uint64_t() const;
/**
* @return true if the object value is < 0
*/
bool isNeg () const noexcept;
/**
* @return true if the object value is > INT64_MAX or < INT64_MIN
*/
bool isBig () const noexcept;
/**
* @return true if a calculation has produced a result of larger
* magnitude than can be contained in the 128 bits available.
*/
bool isOverflow () const noexcept;
/**
* @return true if an illegal calculation has occured.
*/
bool isNan () const noexcept;
/**
* @return true if the object represents 0.
*/
bool isZero() const noexcept;
/**
* Fills a supplied buffer with a representation of the number in base 10. If
* the QofInt128 is overflowed or NaN it will contain the words "Overflow" or
* "NaN" respectively.
*
* @param buf char[41], 39 digits plus sign and trailing 0.
* @return pointer to the buffer for convenience
*/
char* asCharBufR(char* buf) const noexcept;
QofInt128 operator-() const noexcept;
explicit operator bool() const noexcept;
QofInt128& operator++ () noexcept;
QofInt128 operator++ (int) noexcept;
QofInt128& operator+= (const QofInt128& b) noexcept;
QofInt128& operator-= (const QofInt128& b) noexcept;
QofInt128& operator*= (const QofInt128& b) noexcept;
QofInt128& operator/= (const QofInt128& b) noexcept;
QofInt128& operator%= (const QofInt128& b) noexcept;
};
static const QofInt128 k_qofInt128_Max {UINT64_MAX, UINT64_MAX, QofInt128::pos};
static const QofInt128 k_qofInt128_Min {UINT64_MAX, UINT64_MAX, QofInt128::neg};
QofInt128 operator+ (QofInt128 a, QofInt128 b) noexcept;
QofInt128 operator- (QofInt128 a, QofInt128 b) noexcept;
QofInt128 operator* (QofInt128 a, QofInt128 b) noexcept;
QofInt128 operator/ (QofInt128 a, QofInt128 b) noexcept;
QofInt128 operator% (QofInt128 a, QofInt128 b) noexcept;
bool operator== (const QofInt128& a, const QofInt128& b) noexcept;
bool operator!= (const QofInt128& a, const QofInt128& b) noexcept;
bool operator<= (const QofInt128& a, const QofInt128& b) noexcept;
bool operator>= (const QofInt128& a, const QofInt128& b) noexcept;
bool operator< (const QofInt128& a, const QofInt128& b) noexcept;
bool operator> (const QofInt128& a, const QofInt128& b) noexcept;
std::ostream& operator<< (std::ostream&, const QofInt128&) noexcept;
/** Compute the greatest common denominator of two integers
*/
QofInt128 gcd (int64_t a, int64_t b);
/** Compute the least common multiple of two integers
*/
QofInt128 lcm (int64_t a, int64_t b);
#endif //QOFINT128_H
/** @} */

View File

@ -33,6 +33,23 @@ check_PROGRAMS = \
TESTS = ${check_PROGRAMS}
if WITH_GOOGLE_TEST
test_qofint128_SOURCES = \
$(top_srcdir)/${MODULEPATH}/qofint128.cpp \
${GTEST_ROOT}/src/gtest_main.cc \
gtest-qofint128.cpp
test_qofint128_CPPFLAGS = \
-I${GTEST_HEADERS} \
${GLIB_CFLAGS}
test_qofint128_LDADD = \
${GLIB_LIBS} \
$(top_builddir)/src/test-core/libgtest.a
check_PROGRAMS += test-qofint128
endif
test_qofdir = ${GNC_LIBEXECDIR}/${MODULEPATH}/test
#The tests might require more libraries, but try to keep them

View File

@ -0,0 +1,319 @@
/********************************************************************
* gtest-qofmath128.cpp -- unit tests for the QofInt128 class *
* Copyright (C) 2014 John Ralls <jralls@ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
* *
*******************************************************************/
#include <gtest/gtest.h>
#include "../qofint128.hpp"
TEST(qofint128_constructors, test_default_constructor)
{
QofInt128 value {};
EXPECT_EQ (true, value.isZero());
EXPECT_EQ (false, value.isNeg());
EXPECT_EQ (false, value.isBig());
EXPECT_EQ (false, value.isOverflow());
EXPECT_EQ (false, value.isNan());
}
TEST(qofint128_constructors, test_single_arg_constructor)
{
QofInt128 value1 (INT64_C(0));
EXPECT_EQ (true, value1.isZero());
EXPECT_EQ (false, value1.isNeg());
EXPECT_EQ (false, value1.isBig());
EXPECT_EQ (false, value1.isOverflow());
EXPECT_EQ (false, value1.isNan());
QofInt128 value2 (INT64_C(567894392130486208));
EXPECT_EQ (false, value2.isZero());
EXPECT_EQ (false, value2.isNeg());
EXPECT_EQ (false, value2.isBig());
EXPECT_EQ (false, value2.isOverflow());
EXPECT_EQ (false, value2.isNan());
QofInt128 value3 (INT64_C(-567894392130486208));
EXPECT_EQ (false, value3.isZero());
EXPECT_EQ (true, value3.isNeg());
EXPECT_EQ (false, value3.isBig());
EXPECT_EQ (false, value3.isOverflow());
EXPECT_EQ (false, value3.isNan());
QofInt128 value4 (UINT64_C(13567894392130486208));
EXPECT_EQ (false, value4.isZero());
EXPECT_EQ (false, value4.isNeg());
EXPECT_EQ (true, value4.isBig());
EXPECT_EQ (false, value4.isOverflow());
EXPECT_EQ (false, value4.isNan());
}
TEST(qofint128_constructors, test_double_arg_constructor)
{
QofInt128 value1 (INT64_C(0), INT64_C(0));
EXPECT_EQ (true, value1.isZero());
EXPECT_EQ (false, value1.isNeg());
EXPECT_EQ (false, value1.isBig());
EXPECT_EQ (false, value1.isOverflow());
EXPECT_EQ (false, value1.isNan());
QofInt128 value2 (INT64_C(0), INT64_C(567894392130486208));
EXPECT_EQ (false, value2.isZero());
EXPECT_EQ (false, value2.isNeg());
EXPECT_EQ (false, value2.isBig());
EXPECT_EQ (false, value2.isOverflow());
EXPECT_EQ (false, value2.isNan());
QofInt128 value3 (INT64_C(567894392130486208), INT64_C(0));
EXPECT_EQ (false, value3.isZero());
EXPECT_EQ (false, value3.isNeg());
EXPECT_EQ (true, value3.isBig());
EXPECT_EQ (false, value3.isOverflow());
EXPECT_EQ (false, value3.isNan());
QofInt128 value4 (INT64_C(567894392130486208), INT64_C(567894392130486208));
EXPECT_EQ (false, value4.isZero());
EXPECT_EQ (false, value4.isNeg());
EXPECT_EQ (true, value4.isBig());
EXPECT_EQ (false, value4.isOverflow());
EXPECT_EQ (false, value4.isNan());
QofInt128 value5 (INT64_C(567894392130486208),
INT64_C(-567894392130486208));
EXPECT_EQ (false, value5.isZero());
EXPECT_EQ (false, value5.isNeg());
EXPECT_EQ (true, value5.isBig());
EXPECT_EQ (false, value5.isOverflow());
EXPECT_EQ (false, value5.isNan());
QofInt128 value6 (INT64_C(-567894392130486208),
INT64_C(567894392130486208));
EXPECT_EQ (false, value6.isZero());
EXPECT_EQ (true, value6.isNeg());
EXPECT_EQ (true, value6.isBig());
EXPECT_EQ (false, value6.isOverflow());
EXPECT_EQ (false, value6.isNan());
QofInt128 value7 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), QofInt128::pos);
EXPECT_EQ (false, value7.isZero());
EXPECT_EQ (false, value7.isNeg());
EXPECT_EQ (true, value7.isBig());
EXPECT_EQ (false, value7.isOverflow());
EXPECT_EQ (false, value7.isNan());
QofInt128 value8 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), QofInt128::neg);
EXPECT_EQ (false, value8.isZero());
EXPECT_EQ (true, value8.isNeg());
EXPECT_EQ (true, value8.isBig());
EXPECT_EQ (false, value8.isOverflow());
EXPECT_EQ (false, value8.isNan());
QofInt128 value9 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), QofInt128::overflow);
EXPECT_EQ (false, value9.isZero());
EXPECT_EQ (false, value9.isNeg());
EXPECT_EQ (true, value9.isBig());
EXPECT_EQ (true, value9.isOverflow());
EXPECT_EQ (false, value9.isNan());
QofInt128 value10 (UINT64_C(13567894392130486208),
UINT64_C(13567894392130486208), QofInt128::NaN);
EXPECT_EQ (false, value10.isZero());
EXPECT_EQ (false, value10.isNeg());
EXPECT_EQ (true, value10.isBig());
EXPECT_EQ (false, value10.isOverflow());
EXPECT_EQ (true, value10.isNan());
}
TEST(qofint128_functions, test_int_functions)
{
int64_t arg {INT64_C(567894392130486208)};
int64_t narg {INT64_C(-567894392130486208)};
uint64_t uarg {UINT64_C(13567894392130486208)};
QofInt128 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));
QofInt128 value2 (UINT64_C(0), uarg);
EXPECT_THROW (static_cast<int64_t>(value2), std::overflow_error);
EXPECT_EQ (uarg, static_cast<uint64_t>(value2));
QofInt128 value3 (UINT64_C(0), uarg, QofInt128::neg);
EXPECT_THROW (static_cast<int64_t>(value3), std::underflow_error);
EXPECT_THROW (static_cast<uint64_t>(value3), std::underflow_error);
QofInt128 value4 (UINT64_C(0), uarg, QofInt128::overflow);
EXPECT_THROW (static_cast<int64_t>(value4), std::overflow_error);
EXPECT_THROW (static_cast<uint64_t>(value4), std::overflow_error);
QofInt128 value5 (UINT64_C(0), uarg, QofInt128::NaN);
EXPECT_THROW (static_cast<int64_t>(value5), std::overflow_error);
EXPECT_THROW (static_cast<uint64_t>(value5), std::overflow_error);
QofInt128 value6 (INT64_C(1), arg);
EXPECT_THROW (static_cast<int64_t>(value6), std::overflow_error);
EXPECT_EQ (arg + (UINT64_C(0x1) << 63), static_cast<uint64_t>(value6));
QofInt128 value7 (INT64_C(-1), arg);
EXPECT_EQ (-static_cast<int64_t>((UINT64_C(0x1) << 63) - arg),
static_cast<int64_t>(value7));
EXPECT_THROW (static_cast<uint64_t>(value7), std::underflow_error);
QofInt128 value8 (INT64_C(0), narg);
EXPECT_EQ (narg, static_cast<int64_t>(value8));
EXPECT_THROW (static_cast<uint64_t>(value8), std::underflow_error);
QofInt128 value9 (INT64_C(1), narg);
EXPECT_EQ (static_cast<int64_t>((UINT64_C(0x1) << 63) + narg),
static_cast<int64_t>(value9));
EXPECT_EQ ((UINT64_C(0x1) << 63) + narg, static_cast<uint64_t>(value9));
QofInt128 value10 (INT64_C(-2), arg);
EXPECT_THROW (static_cast<int64_t>(value10), std::underflow_error);
EXPECT_THROW (static_cast<uint64_t>(value10), std::underflow_error);
}
TEST(qofint128_functions, test_compare)
{
int64_t barg {INT64_C(567894392130486208)};
int64_t nbarg {INT64_C(-567894392130486208)};
int64_t sarg {INT64_C(567894392130486207)};
int64_t nsarg {INT64_C(-567894392130486207)};
QofInt128 big (barg);
QofInt128 small (sarg);
QofInt128 neg_big (nbarg);
QofInt128 neg_small (nsarg);
QofInt128 really_big (barg, sarg);
QofInt128 slightly_bigger (barg, barg);
QofInt128 not_as_big (sarg, barg);
QofInt128 a_little_smaller (sarg, sarg);
QofInt128 neg_really_big (barg, sarg, QofInt128::neg);
QofInt128 neg_slightly_bigger (barg, barg, QofInt128::neg);
QofInt128 neg_not_as_big (sarg, barg, QofInt128::neg);
QofInt128 neg_a_little_smaller (sarg, sarg, QofInt128::neg);
QofInt128 overflowed (INT64_C(0), INT64_C(0), QofInt128::overflow);
QofInt128 not_a_number (INT64_C(0), INT64_C(0), QofInt128::NaN);
EXPECT_EQ (-9, overflowed.cmp (big));
EXPECT_EQ (-9, not_a_number.cmp (big));
EXPECT_EQ (9, big.cmp (overflowed));
EXPECT_EQ (9, big.cmp (not_a_number));
EXPECT_EQ (-1, neg_big.cmp(big));
EXPECT_EQ (-1, neg_big.cmp(small));
EXPECT_EQ (-1, neg_small.cmp(big));
EXPECT_EQ (-2, neg_big.cmp(neg_small));
EXPECT_EQ (2, neg_small.cmp(neg_big));
EXPECT_EQ (-4, small.cmp(big));
EXPECT_EQ (4, big.cmp(small));
EXPECT_EQ (1, small.cmp(neg_big));
EXPECT_EQ (1, big.cmp(neg_small));
EXPECT_EQ (-5, big.cmp(a_little_smaller));
EXPECT_EQ (1, big.cmp(neg_a_little_smaller));
EXPECT_EQ (-4, really_big.cmp(slightly_bigger));
EXPECT_EQ (5, really_big.cmp(not_as_big));
EXPECT_EQ (-5, big.cmp(really_big));
EXPECT_EQ (-1, neg_really_big.cmp(big));
EXPECT_EQ (-1, neg_really_big.cmp(not_as_big));
EXPECT_EQ (-1, neg_really_big.cmp(slightly_bigger));
EXPECT_EQ (-3, neg_really_big.cmp(neg_not_as_big));
EXPECT_EQ (-3, neg_really_big.cmp(neg_a_little_smaller));
EXPECT_EQ (2, neg_really_big.cmp(neg_slightly_bigger));
EXPECT_EQ (3, neg_not_as_big.cmp(neg_really_big));
EXPECT_EQ (-2, neg_not_as_big.cmp(neg_a_little_smaller));
EXPECT_EQ (-3, neg_really_big.cmp(neg_a_little_smaller));
EXPECT_EQ (0, neg_really_big.cmp(QofInt128(barg, sarg, QofInt128::neg)));
EXPECT_EQ (0, really_big.cmp(QofInt128(barg, sarg)));
EXPECT_EQ (0, really_big.cmp(QofInt128(barg, sarg, QofInt128::pos)));
EXPECT_EQ (0, really_big.cmp(-neg_really_big));
EXPECT_EQ (0, neg_really_big.cmp(-really_big));
}
TEST(qofint128_functions, stream_output)
{
int64_t barg {INT64_C(567894392130486208)};
int64_t sarg {INT64_C(567894392130486207)};
int64_t nsarg {INT64_C(-567894392130486207)};
QofInt128 small (sarg);
QofInt128 neg_small (nsarg);
QofInt128 really_big (barg, sarg);
QofInt128 neg_really_big (barg, sarg, QofInt128::neg);
QofInt128 overflowed (INT64_C(0), INT64_C(0), QofInt128::overflow);
QofInt128 not_a_number (INT64_C(0), INT64_C(0), QofInt128::NaN);
QofInt128 boundary_value (UINT64_C(1), UINT64_MAX);
static const uint8_t char_buf_size {41};
char buf[char_buf_size] {};
EXPECT_STREQ("567894392130486207", small.asCharBufR (buf));
EXPECT_STREQ("-567894392130486207", neg_small.asCharBufR (buf));
EXPECT_STREQ("5237901256262967342410779070006542271", really_big.asCharBufR (buf));
EXPECT_STREQ("-5237901256262967342410779070006542271", neg_really_big.asCharBufR (buf));
EXPECT_STREQ("36893488147419103231", boundary_value.asCharBufR (buf));
EXPECT_STREQ("Overflow", overflowed.asCharBufR (buf));
EXPECT_STREQ("NaN", not_a_number.asCharBufR (buf));
}
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
* barg + uarg = UINT64_MAX
*/
int64_t barg {INT64_C(4878849681579065407)};
int64_t sarg {INT64_C(4344522355275710400)};
uint64_t uarg {UINT64_C(13567894392130486208)};
QofInt128 smallest (sarg + 100);
QofInt128 smaller (barg + 500);
QofInt128 small (uarg);
QofInt128 big (sarg, barg);
QofInt128 bigger (static_cast<uint64_t>(barg), uarg);
QofInt128 biggest (uarg, static_cast<uint64_t>(barg));
EXPECT_EQ (QofInt128(INT64_C(2), INT64_C(499)), small += smaller);
EXPECT_EQ (QofInt128(uarg), small -= smaller);
EXPECT_EQ (QofInt128(static_cast<uint64_t>(barg + sarg/2), UINT64_MAX),
bigger += big);
EXPECT_EQ (QofInt128(static_cast<uint64_t>(barg), uarg), bigger -= big);
bigger += biggest;
EXPECT_EQ (QofInt128(UINT64_MAX, UINT64_MAX), bigger);
bigger += smallest;
EXPECT_TRUE (bigger.isOverflow());
bigger -= biggest;
EXPECT_TRUE (bigger.isOverflow());
}