Make the GncRational and GncNumeric APIs mostly identical.

Leaving string conversion and stream operators out of GncRational.
This commit is contained in:
John Ralls
2017-02-20 15:50:32 -08:00
parent 739c91a4cc
commit ea44b16f54
5 changed files with 343 additions and 206 deletions
+23 -68
View File
@@ -64,17 +64,18 @@ powten (unsigned int exp)
GncNumeric::GncNumeric(GncRational rr)
{
if (rr.m_num.isNan() || rr.m_den.isNan())
/* Can't use isValid here because we want to throw different exceptions. */
if (rr.num().isNan() || rr.denom().isNan())
throw std::underflow_error("Operation resulted in NaN.");
if (rr.m_num.isOverflow() || rr.m_den.isOverflow())
if (rr.num().isOverflow() || rr.denom().isOverflow())
throw std::overflow_error("Operation overflowed a 128-bit int.");
if (rr.m_num.isBig() || rr.m_den.isBig())
if (rr.num().isBig() || rr.denom().isBig())
{
GncRational reduced(rr.reduce());
rr = reduced.round_to_numeric(); // A no-op if it's already small.
}
m_num = static_cast<int64_t>(rr.m_num);
m_den = static_cast<int64_t>(rr.m_den);
m_num = static_cast<int64_t>(rr.num());
m_den = static_cast<int64_t>(rr.denom());
}
GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
@@ -261,18 +262,18 @@ GncNumeric::prepare_conversion(int64_t new_denom) const
GncRational conversion(new_denom, m_den);
auto red_conv = conversion.reduce();
GncInt128 old_num(m_num);
auto new_num = old_num * red_conv.m_num;
auto rem = new_num % red_conv.m_den;
new_num /= red_conv.m_den;
auto new_num = old_num * red_conv.num();
auto rem = new_num % red_conv.denom();
new_num /= red_conv.denom();
if (new_num.isBig())
{
GncRational rr(new_num, new_denom);
GncNumeric nn(rr);
rr = rr.convert<RoundType::truncate>(new_denom);
return {static_cast<int64_t>(rr.m_num), new_denom, 0};
return {static_cast<int64_t>(rr.num()), new_denom, 0};
}
return {static_cast<int64_t>(new_num), static_cast<int64_t>(red_conv.m_den),
static_cast<int64_t>(rem)};
return {static_cast<int64_t>(new_num),
static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
}
int64_t
@@ -331,25 +332,26 @@ GncNumeric::to_decimal(unsigned int max_places) const
rr = rr.convert<RoundType::never>(powten(max_places)); //May throw
/* rr might have gotten reduced a bit too much; if so, put it back: */
unsigned int pwr{1};
for (; pwr <= max_places && !(rr.m_den % powten(pwr)); ++pwr);
for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
auto reduce_to = powten(pwr);
if (rr.m_den % reduce_to)
GncInt128 rr_num(rr.num()), rr_den(rr.denom());
if (rr_den % reduce_to)
{
auto factor(reduce_to / rr.m_den);
rr.m_num *= factor;
rr.m_den *= factor;
auto factor(reduce_to / rr.denom());
rr_num *= factor;
rr_den *= factor;
}
while (rr.m_num % 10 == 0)
while (rr_num % 10 == 0)
{
rr.m_num /= 10;
rr.m_den /= 10;
rr_num /= 10;
rr_den /= 10;
}
try
{
/* Construct from the parts to avoid the GncRational constructor's
* automatic rounding.
*/
return {static_cast<int64_t>(rr.m_num), static_cast<int64_t>(rr.m_den)};
return {static_cast<int64_t>(rr_num), static_cast<int64_t>(rr_den)};
}
catch (const std::invalid_argument& err)
{
@@ -399,12 +401,8 @@ GncNumeric::cmp(GncNumeric b)
auto b_num = b.num();
return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
}
// GncInt128 a_den(m_den), b_den(b.denom());
// auto lcm = a_den.gcd(b_den);
// GncInt128 a_num(m_num * gcd / a_den), b_num(b.num() * gcd / b_den);
// return a_num < b_num ? -1 : b_num < a_num ? 1 : 0;
GncRational an(*this), bn(b);
return (an.m_num * bn.m_den).cmp(bn.m_num * an.m_den);
return an.cmp(bn);
}
GncNumeric
@@ -454,49 +452,6 @@ operator/(GncNumeric a, GncNumeric b)
return static_cast<GncNumeric>(rr);
}
int
cmp(GncNumeric a, GncNumeric b)
{
return a.cmp(b);
}
bool
operator<(GncNumeric a, GncNumeric b)
{
return a.cmp(b) < 0;
}
bool
operator>(GncNumeric a, GncNumeric b)
{
return a.cmp(b) > 0;
}
bool
operator==(GncNumeric a, GncNumeric b)
{
return a.cmp(b) == 0;
}
bool
operator<=(GncNumeric a, GncNumeric b)
{
return a.cmp(b) <= 0;
}
bool
operator>=(GncNumeric a, GncNumeric b)
{
return a.cmp(b) >= 0;
}
bool
operator!=(GncNumeric a, GncNumeric b)
{
return a.cmp(b) != 0;
}
template <typename T, typename I> T
convert(T num, I new_denom, int how)
{
+60 -11
View File
@@ -29,8 +29,8 @@
class GncRational;
/**
* The primary numeric class for representing amounts and values.
/**@ingroup QOF
* @brief The primary numeric class for representing amounts and values.
*
* Calculations are generally performed in 128-bit (by converting to
* GncRational) and reducing the result. If the result would overflow a 64-bit
@@ -255,6 +255,7 @@ public:
* @return -1 if this < b, 0 if ==, 1 if this > b.
*/
int cmp(GncNumeric b);
int cmp(int64_t b) { return cmp(GncNumeric(b, 1)); }
private:
struct round_param
{
@@ -274,7 +275,7 @@ private:
/**
* \defgroup gnc_numeric_arithmetic_operators
*
* @{
* Normal arithmetic operators. The class arithmetic operators are implemented
* in terms of these operators. They use GncRational operators internally then
* call the GncNumeric(GncRational&) constructor which will silently round
@@ -285,12 +286,45 @@ private:
*
* \param a The right-side operand
* \param b The left-side operand
* \return A new GncNumeric computed from the sum.
* \return A GncNumeric computed from the operation.
*/
GncNumeric operator+(GncNumeric a, GncNumeric b);
inline GncNumeric operator+(GncNumeric a, int64_t b)
{
return a + GncNumeric(b, 1);
}
inline GncNumeric operator+(int64_t a, GncNumeric b)
{
return b + GncNumeric(a, 1);
}
GncNumeric operator-(GncNumeric a, GncNumeric b);
inline GncNumeric operator-(GncNumeric a, int64_t b)
{
return a - GncNumeric(b, 1);
}
inline GncNumeric operator-(int64_t a, GncNumeric b)
{
return b - GncNumeric(a, 1);
}
GncNumeric operator*(GncNumeric a, GncNumeric b);
inline GncNumeric operator*(GncNumeric a, int64_t b)
{
return a * GncNumeric(b, 1);
}
inline GncNumeric operator*(int64_t a, GncNumeric b)
{
return b * GncNumeric(a, 1);
}
GncNumeric operator/(GncNumeric a, GncNumeric b);
inline GncNumeric operator/(GncNumeric a, int64_t b)
{
return a / GncNumeric(b, 1);
}
inline GncNumeric operator/(int64_t a, GncNumeric b)
{
return b / GncNumeric(a, 1);
}
/** @} */
/**
* std::stream output operator. Uses standard integer operator<< so should obey
* locale rules. Numbers are presented as integers if the denominator is 1, as a
@@ -346,18 +380,33 @@ std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>&
/**
* @return -1 if a < b, 0 if a == b, 1 if a > b.
*/
int cmp(GncNumeric a, GncNumeric b);
inline int cmp(GncNumeric a, GncNumeric b) { return a.cmp(b); }
inline int cmp(GncNumeric a, int64_t b) { return a.cmp(b); }
inline int cmp(int64_t a, GncNumeric b) { return GncNumeric(a, 1).cmp(b); }
/**
* \defgroup gnc_numeric_comparison_operators
* @{
* Standard comparison operators, which do what one would expect.
*/
bool operator<(GncNumeric a, GncNumeric b);
bool operator>(GncNumeric a, GncNumeric b);
bool operator==(GncNumeric a, GncNumeric b);
bool operator<=(GncNumeric a, GncNumeric b);
bool operator>=(GncNumeric a, GncNumeric b);
bool operator!=(GncNumeric a, GncNumeric b);
inline bool operator<(GncNumeric a, GncNumeric b) { return cmp(a, b) < 0; }
inline bool operator<(GncNumeric a, int64_t b) { return cmp(a, b) < 0; }
inline bool operator<(int64_t a, GncNumeric b) { return cmp(a, b) < 0; }
inline bool operator>(GncNumeric a, GncNumeric b) { return cmp(a, b) > 0; }
inline bool operator>(GncNumeric a, int64_t b) { return cmp(a, b) > 0; }
inline bool operator>(int64_t a, GncNumeric b) { return cmp(a, b) > 0; }
inline bool operator==(GncNumeric a, GncNumeric b) { return cmp(a, b) == 0; }
inline bool operator==(GncNumeric a, int64_t b) { return cmp(a, b) == 0; }
inline bool operator==(int64_t a, GncNumeric b) { return cmp(a, b) == 0; }
inline bool operator<=(GncNumeric a, GncNumeric b) { return cmp(a, b) <= 0; }
inline bool operator<=(GncNumeric a, int64_t b) { return cmp(a, b) <= 0; }
inline bool operator<=(int64_t a, GncNumeric b) { return cmp(a, b) <= 0; }
inline bool operator>=(GncNumeric a, GncNumeric b) { return cmp(a, b) >= 0; }
inline bool operator>=(GncNumeric a, int64_t b) { return cmp(a, b) >= 0; }
inline bool operator>=(int64_t a, GncNumeric b) { return cmp(a, b) >= 0; }
inline bool operator!=(GncNumeric a, GncNumeric b) { return cmp(a, b) != 0; }
inline bool operator!=(GncNumeric a, int64_t b) { return cmp(a, b) != 0; }
inline bool operator!=(int64_t a, GncNumeric b) { return cmp(a, b) != 0; }
/** @} */
/**
* Convenience function to quickly return 10**digits.
+98 -82
View File
@@ -78,91 +78,27 @@ GncRational::operator gnc_numeric () const noexcept
GncRational
GncRational::operator-() const noexcept
{
GncRational b(*this);
b.m_num = - b.m_num;
return b;
return GncRational(-m_num, m_den);
}
GncRational&
GncRational::inv () noexcept
GncRational
GncRational::inv () const noexcept
{
if (m_den < 0)
{
m_num *= -m_den;
m_den = 1;
}
std::swap(m_num, m_den);
if (m_num == 0)
return *this;
if (m_num < 0)
return GncRational(-m_den, -m_num);
return GncRational(m_den, m_num);
}
reduce();
GncRational
GncRational::abs() const noexcept
{
if (m_num < 0)
return -*this;
return *this;
}
GncRational
operator+(GncRational a, GncRational b)
{
if (!(a.valid() && b.valid()))
throw std::range_error("Operator+ called with out-of-range operand.");
GncInt128 lcm = a.m_den.lcm(b.m_den);
GncInt128 num(a.m_num * lcm / a.m_den + b.m_num * lcm / b.m_den);
if (!(lcm.valid() && num.valid()))
throw std::overflow_error("Operator+ overflowed.");
GncRational retval(num, lcm);
return retval;
}
GncRational
operator-(GncRational a, GncRational b)
{
GncRational retval = a + (-b);
return retval;
}
GncRational
operator*(GncRational a, GncRational b)
{
if (!(a.valid() && b.valid()))
throw std::range_error("Operator* called with out-of-range operand.");
GncInt128 num (a.m_num * b.m_num), den(a.m_den * b.m_den);
if (!(num.valid() && den.valid()))
throw std::overflow_error("Operator* overflowed.");
GncRational retval(num, den);
return retval;
}
GncRational
operator/(GncRational a, GncRational b)
{
if (!(a.valid() && b.valid()))
throw std::range_error("Operator/ called with out-of-range operand.");
if (b.m_num == 0)
throw std::underflow_error("Divide by 0.");
if (b.m_num.isNeg())
{
a.m_num = -a.m_num;
b.m_num = -b.m_num;
}
/* q = (a_num * b_den)/(b_num * a_den). If a_den == b_den they cancel out
* and it's just a_num/b_num.
*/
if (a.m_den == b.m_den)
return GncRational(a.m_num, b.m_num);
/* Protect against possibly preventable overflow: */
if (a.m_num.isBig() || a.m_den.isBig() ||
b.m_num.isBig() || b.m_den.isBig())
{
GncInt128 gcd = b.m_den.gcd(a.m_den);
b.m_den /= gcd;
a.m_den /= gcd;
}
GncInt128 num(a.m_num * b.m_den), den(a.m_den * b.m_num);
if (!(num.valid() && den.valid()))
throw std::overflow_error("Operator/ overflowed.");
return GncRational(num, den);
}
void
GncRational::operator+=(GncRational b)
{
@@ -191,6 +127,19 @@ GncRational::operator/=(GncRational b)
*this = std::move(new_val);
}
int
GncRational::cmp(GncRational b)
{
if (m_den == b.denom())
{
auto b_num = b.num();
return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
}
auto gcd = m_den.gcd(b.denom());
GncInt128 a_num(m_num * b.denom() / gcd), b_num(b.num() * m_den / gcd);
return a_num < b_num ? -1 : b_num < a_num ? 1 : 0;
}
GncRational::round_param
GncRational::prepare_conversion (GncInt128 new_denom) const
{
@@ -199,10 +148,10 @@ GncRational::prepare_conversion (GncInt128 new_denom) const
GncRational conversion(new_denom, m_den);
auto red_conv = conversion.reduce();
GncInt128 old_num(m_num);
auto new_num = old_num * red_conv.m_num;
auto rem = new_num % red_conv.m_den;
new_num /= red_conv.m_den;
return {new_num, red_conv.m_den, rem};
auto new_num = old_num * red_conv.num();
auto rem = new_num % red_conv.denom();
new_num /= red_conv.denom();
return {new_num, red_conv.denom(), rem};
}
GncInt128
@@ -267,3 +216,70 @@ GncRational::round_to_numeric() const
new_v = new_v.convert<RoundType::half_down>(m_den / divisor);
return new_v;
}
GncRational
operator+(GncRational a, GncRational b)
{
if (!(a.valid() && b.valid()))
throw std::range_error("Operator+ called with out-of-range operand.");
GncInt128 lcm = a.denom().lcm(b.denom());
GncInt128 num(a.num() * lcm / a.denom() + b.num() * lcm / b.denom());
if (!(lcm.valid() && num.valid()))
throw std::overflow_error("Operator+ overflowed.");
GncRational retval(num, lcm);
return retval;
}
GncRational
operator-(GncRational a, GncRational b)
{
GncRational retval = a + (-b);
return retval;
}
GncRational
operator*(GncRational a, GncRational b)
{
if (!(a.valid() && b.valid()))
throw std::range_error("Operator* called with out-of-range operand.");
GncInt128 num (a.num() * b.num()), den(a.denom() * b.denom());
if (!(num.valid() && den.valid()))
throw std::overflow_error("Operator* overflowed.");
GncRational retval(num, den);
return retval;
}
GncRational
operator/(GncRational a, GncRational b)
{
if (!(a.valid() && b.valid()))
throw std::range_error("Operator/ called with out-of-range operand.");
auto a_num = a.num(), b_num = b.num(), a_den = a.denom(), b_den = b.denom();
if (b_num == 0)
throw std::underflow_error("Divide by 0.");
if (b_num.isNeg())
{
a_num = -a_num;
b_num = -b_num;
}
/* q = (a_num * b_den)/(b_num * a_den). If a_den == b_den they cancel out
* and it's just a_num/b_num.
*/
if (a_den == b_den)
return GncRational(a_num, b_num);
/* Protect against possibly preventable overflow: */
if (a_num.isBig() || a_den.isBig() ||
b_num.isBig() || b_den.isBig())
{
GncInt128 gcd = b_den.gcd(a_den);
b_den /= gcd;
a_den /= gcd;
}
GncInt128 num(a_num * b_den), den(a_den * b_num);
if (!(num.valid() && den.valid()))
throw std::overflow_error("Operator/ overflowed.");
return GncRational(num, den);
}
+126 -9
View File
@@ -34,16 +34,43 @@ enum class DenomType;
/** @ingroup QOF
* @brief Rational number class using GncInt128 for the numerator
* and denominator.
*
* This class provides far greater overflow protection compared to GncNumeric at
* the expense of doubling the size, so GncNumeric is preferred for storage into
* objects. Furthermore the backends are not able to store GncRational numbers;
* storage in SQL would require using BLOBs which would preclude calculations in
* queries. GncRational exists *primarily* as a more overflow-resistant
* calculation facility for GncNumeric. It's available for cases where one needs
* an error instead of an automatically rounded value for a calculation that
* produces a result that won't fit into an int64 without rounding.
* Errors: Errors are signalled by exceptions as follows:
* * A zero denominator will raise a std::invalid_argument.
* * Division by zero will raise a std::underflow_error.
* * Overflowing 128 bits will raise a std::overflow_error.
* * Failure to convert a number as specified by the arguments to convert() will
* raise a std::domain_error.
*
*/
class GncRational
{
public:
/**
* Default constructor provides the zero value.
*/
GncRational() : m_num(0), m_den(1) {}
GncRational (gnc_numeric n) noexcept;
GncRational(GncNumeric n) noexcept;
/**
* GncInt128 constructor. This will take any flavor of built-in integer
* thanks to implicit construction of the GncInt128s.
*/
GncRational (GncInt128 num, GncInt128 den) noexcept
: m_num(num), m_den(den) {}
/** Convenience constructor from the C API's gnc_numeric. */
GncRational (gnc_numeric n) noexcept;
/** GncNumeric constructor. */
GncRational(GncNumeric n) noexcept;
GncRational(const GncRational& rhs) = default;
GncRational(GncRational&& rhs) = default;
GncRational& operator=(const GncRational& rhs) = default;
@@ -122,6 +149,10 @@ public:
params.rem, RT2T<RT>()), new_denom);
}
/** Numerator accessor */
GncInt128 num() { return m_num; }
/** Denominator accessor */
GncInt128 denom() { return m_den; }
/** @defgroup gnc_rational_mutators
* @{
* Standard mutating arithmetic operators.
@@ -132,10 +163,17 @@ public:
void operator/=(GncRational b);
/** @} */
/** Inverts the number, equivalent of /= {1, 1} */
GncRational& inv() noexcept;
GncRational inv() const noexcept;
/** Absolute value; return value is always >= 0 and of same magnitude. */
GncRational abs() const noexcept;
/** Compare function
*
* @param b GncNumeric or integer value to compare to.
* @return -1 if < b, 0 if equal, 1 if > b.
*/
int cmp(GncRational b);
int cmp(GncInt128 b) { return cmp(GncRational(b, 1)); }
GncInt128 m_num;
GncInt128 m_den;
private:
struct round_param
{
@@ -152,11 +190,90 @@ private:
* finish computing a GncNumeric with the new denominator.
*/
round_param prepare_conversion(GncInt128 new_denom) const;
GncInt128 m_num;
GncInt128 m_den;
};
GncRational operator+(GncRational a, GncRational b);
GncRational operator-(GncRational a, GncRational b);
GncRational operator*(GncRational a, GncRational b);
GncRational operator/(GncRational a, GncRational b);
/**
* @return -1 if a < b, 0 if a == b, 1 if a > b.
*/
inline int cmp(GncRational a, GncRational b) { return a.cmp(b); }
inline int cmp(GncRational a, GncInt128 b) { return a.cmp(b); }
inline int cmp(GncInt128 a, GncRational b) { return GncRational(a, 1).cmp(b); }
/**
* \defgroup gnc_rational_comparison_operators
* @{
* Standard comparison operators, which do what one would expect.
*/
inline bool operator<(GncRational a, GncRational b) { return cmp(a, b) < 0; }
inline bool operator<(GncRational a, GncInt128 b) { return cmp(a, b) < 0; }
inline bool operator<(GncInt128 a, GncRational b) { return cmp(a, b) < 0; }
inline bool operator>(GncRational a, GncRational b) { return cmp(a, b) > 0; }
inline bool operator>(GncRational a, GncInt128 b) { return cmp(a, b) > 0; }
inline bool operator>(GncInt128 a, GncRational b) { return cmp(a, b) > 0; }
inline bool operator==(GncRational a, GncRational b) { return cmp(a, b) == 0; }
inline bool operator==(GncRational a, GncInt128 b) { return cmp(a, b) == 0; }
inline bool operator==(GncInt128 a, GncRational b) { return cmp(a, b) == 0; }
inline bool operator<=(GncRational a, GncRational b) { return cmp(a, b) <= 0; }
inline bool operator<=(GncRational a, GncInt128 b) { return cmp(a, b) <= 0; }
inline bool operator<=(GncInt128 a, GncRational b) { return cmp(a, b) <= 0; }
inline bool operator>=(GncRational a, GncRational b) { return cmp(a, b) >= 0; }
inline bool operator>=(GncRational a, GncInt128 b) { return cmp(a, b) >= 0; }
inline bool operator>=(GncInt128 a, GncRational b) { return cmp(a, b) >= 0; }
inline bool operator!=(GncRational a, GncRational b) { return cmp(a, b) != 0; }
inline bool operator!=(GncRational a, GncInt128 b) { return cmp(a, b) != 0; }
inline bool operator!=(GncInt128 a, GncRational b) { return cmp(a, b) != 0; }
/** @} */
/**
* \defgroup gnc_rational_arithmetic_operators
*
* Normal arithmetic operators. The class arithmetic operators are implemented
* in terms of these operators.
*
* These operators can throw std::overflow_error, std::underflow_error, or
* std::invalid argument as indicated in the class documentation.
*
* \param a The right-side operand
* \param b The left-side operand
* \return A GncRational computed from the operation.
*/
GncRational operator+(GncRational a, GncRational b);
inline GncRational operator+(GncRational a, GncInt128 b)
{
return a + GncRational(b, 1);
}
inline GncRational operator+(GncInt128 a, GncRational b)
{
return b + GncRational(a, 1);
}
GncRational operator-(GncRational a, GncRational b);
inline GncRational operator-(GncRational a, GncInt128 b)
{
return a - GncRational(b, 1);
}
inline GncRational operator-(GncInt128 a, GncRational b)
{
return b - GncRational(a, 1);
}
GncRational operator*(GncRational a, GncRational b);
inline GncRational operator*(GncRational a, GncInt128 b)
{
return a * GncRational(b, 1);
}
inline GncRational operator*(GncInt128 a, GncRational b)
{
return b * GncRational(a, 1);
}
GncRational operator/(GncRational a, GncRational b);
inline GncRational operator/(GncRational a, GncInt128 b)
{
return a / GncRational(b, 1);
}
inline GncRational operator/(GncInt128 a, GncRational b)
{
return b / GncRational(a, 1);
}
/** @} */
#endif //__GNC_RATIONAL_HPP__
+36 -36
View File
@@ -30,8 +30,8 @@ TEST(gncrational_constructors, test_default_constructor)
{
EXPECT_NO_THROW({
GncRational value;
EXPECT_EQ(value.m_num, 0);
EXPECT_EQ(value.m_den, 1);
EXPECT_EQ(value.num(), 0);
EXPECT_EQ(value.denom(), 1);
});
}
@@ -40,8 +40,8 @@ TEST(gncrational_constructors, test_gnc_numeric_constructor)
gnc_numeric input = gnc_numeric_create(123, 456);
EXPECT_NO_THROW({
GncRational value(input);
EXPECT_EQ(input.num, value.m_num);
EXPECT_EQ(input.denom, value.m_den);
EXPECT_EQ(input.num, value.num());
EXPECT_EQ(input.denom, value.denom());
});
}
@@ -50,8 +50,8 @@ TEST(gncrational_constructors, test_gnc_int128_constructor)
GncInt128 num(123), denom(456);
EXPECT_NO_THROW({
GncRational value(num, denom);
EXPECT_EQ(123, value.m_num);
EXPECT_EQ(456, value.m_den);
EXPECT_EQ(123, value.num());
EXPECT_EQ(456, value.denom());
});
}
@@ -60,8 +60,8 @@ TEST(gncrational_constructors, test_implicit_int_constructor)
int num(123), denom(456);
EXPECT_NO_THROW({
GncRational value(num, denom);
EXPECT_EQ(123, value.m_num);
EXPECT_EQ(456, value.m_den);
EXPECT_EQ(123, value.num());
EXPECT_EQ(456, value.denom());
});
}
@@ -71,11 +71,11 @@ TEST(gncrational_operators, test_addition)
GncRational a(123456789987654321, 1000000000);
GncRational b(65432198765432198, 100000000);
GncRational c = a + b;
EXPECT_EQ (777778777641976301, c.m_num);
EXPECT_EQ (1000000000, c.m_den);
EXPECT_EQ (777778777641976301, c.num());
EXPECT_EQ (1000000000, c.denom());
a += b;
EXPECT_EQ (777778777641976301, a.m_num);
EXPECT_EQ (1000000000, a.m_den);
EXPECT_EQ (777778777641976301, a.num());
EXPECT_EQ (1000000000, a.denom());
});
}
@@ -85,17 +85,17 @@ TEST(gncrational_operators, test_subtraction)
GncRational a(123456789987654321, 1000000000);
GncRational b(65432198765432198, 100000000);
GncRational c = a - b;
EXPECT_EQ (-530865197666667659, c.m_num);
EXPECT_TRUE(c.m_num.isNeg());
EXPECT_EQ (1000000000, c.m_den);
EXPECT_EQ (-530865197666667659, c.num());
EXPECT_TRUE(c.num().isNeg());
EXPECT_EQ (1000000000, c.denom());
c = b - a;
EXPECT_EQ (530865197666667659, c.m_num);
EXPECT_FALSE(c.m_num.isNeg());
EXPECT_EQ (1000000000, c.m_den);
EXPECT_EQ (530865197666667659, c.num());
EXPECT_FALSE(c.num().isNeg());
EXPECT_EQ (1000000000, c.denom());
a -= b;
EXPECT_EQ (-530865197666667659, a.m_num);
EXPECT_TRUE(a.m_num.isNeg());
EXPECT_EQ (1000000000, a.m_den);
EXPECT_EQ (-530865197666667659, a.num());
EXPECT_TRUE(a.num().isNeg());
EXPECT_EQ (1000000000, a.denom());
});
}
@@ -106,12 +106,12 @@ TEST(gncrational_operators, test_multiplication)
GncRational b(65432198765432198, 100000000);
GncRational c = a * b;
EXPECT_EQ (GncInt128(UINT64_C(437911925765117),
UINT64_C(8081008345983448486)), c.m_num);
EXPECT_EQ (100000000000000000, c.m_den);
UINT64_C(8081008345983448486)), c.num());
EXPECT_EQ (100000000000000000, c.denom());
a *= b;
EXPECT_EQ (GncInt128(UINT64_C(437911925765117),
UINT64_C(8081008345983448486)), a.m_num);
EXPECT_EQ (100000000000000000, a.m_den);
UINT64_C(8081008345983448486)), a.num());
EXPECT_EQ (100000000000000000, a.denom());
});
}
@@ -122,14 +122,14 @@ TEST(gncrational_operators, test_division)
GncRational b(65432198765432198, 100000000);
GncRational c = a / b;
EXPECT_EQ (GncInt128(UINT64_C(669260),
UINT64_C(11059994577585475840)), c.m_num);
UINT64_C(11059994577585475840)), c.num());
EXPECT_EQ (GncInt128(UINT64_C(3547086),
UINT64_C(11115994079396609024)), c.m_den);
UINT64_C(11115994079396609024)), c.denom());
a /= b;
EXPECT_EQ (GncInt128(UINT64_C(669260),
UINT64_C(11059994577585475840)), a.m_num);
UINT64_C(11059994577585475840)), a.num());
EXPECT_EQ (GncInt128(UINT64_C(3547086),
UINT64_C(11115994079396609024)), a.m_den);
UINT64_C(11115994079396609024)), a.denom());
});
}
@@ -148,12 +148,12 @@ TEST(gncrational_functions, test_round_to_numeric)
expected = expected.convert<RoundType::bankers>(100);
auto rounded = c.round_to_numeric();
rounded = rounded.convert<RoundType::bankers>(100);
EXPECT_EQ(0, expected.m_num - rounded.m_num);
EXPECT_FALSE(rounded.m_num.isBig());
EXPECT_FALSE(rounded.m_den.isBig());
EXPECT_FALSE(rounded.m_num.isNan());
EXPECT_FALSE(rounded.m_den.isNan());
EXPECT_FALSE(rounded.m_num.isOverflow());
EXPECT_FALSE(rounded.m_den.isOverflow());
EXPECT_EQ(0, expected.num() - rounded.num());
EXPECT_FALSE(rounded.num().isBig());
EXPECT_FALSE(rounded.denom().isBig());
EXPECT_FALSE(rounded.num().isNan());
EXPECT_FALSE(rounded.denom().isNan());
EXPECT_FALSE(rounded.num().isOverflow());
EXPECT_FALSE(rounded.denom().isOverflow());
}
}