mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Begin QofInt128 class.
Constructs, adds & subtracts, prints, and compares. Includes unit tests.
This commit is contained in:
parent
9d029d7f82
commit
d4fdd9ef17
349
src/libqof/qof/qofint128.cpp
Normal file
349
src/libqof/qof/qofint128.cpp
Normal 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;
|
||||
}
|
||||
|
193
src/libqof/qof/qofint128.hpp
Normal file
193
src/libqof/qof/qofint128.hpp
Normal 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
|
||||
|
||||
/** @} */
|
@ -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
|
||||
|
319
src/libqof/qof/test/gtest-qofint128.cpp
Normal file
319
src/libqof/qof/test/gtest-qofint128.cpp
Normal 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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user