mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Implement basic arithmetic operators for GncRational.
The operators do no rounding or reducing yet.
This commit is contained in:
@@ -83,17 +83,114 @@ GncRational::inv () noexcept
|
||||
return *this;
|
||||
}
|
||||
|
||||
GncRational
|
||||
operator+(GncRational a, GncRational b)
|
||||
{
|
||||
if (a.m_error || b.m_error)
|
||||
{
|
||||
if (b.m_error)
|
||||
return GncRational(0, 1, b.m_error);
|
||||
return GncRational(0, 1, a.m_error);
|
||||
}
|
||||
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.isOverflow() || lcm.isNan() || num.isOverflow() || num.isNan())
|
||||
return GncRational(0, 1, GNC_ERROR_OVERFLOW);
|
||||
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.m_error || b.m_error)
|
||||
{
|
||||
if (b.m_error)
|
||||
return GncRational(0, 1, b.m_error);
|
||||
return GncRational(0, 1, a.m_error);
|
||||
}
|
||||
GncInt128 num (a.m_num * b.m_num), den(a.m_den * b.m_den);
|
||||
if (num.isOverflow() || num.isNan() || den.isOverflow() || den.isNan())
|
||||
return GncRational(0, 1, GNC_ERROR_OVERFLOW);
|
||||
GncRational retval(num, den);
|
||||
return retval;
|
||||
}
|
||||
|
||||
GncRational
|
||||
operator/(GncRational a, GncRational b)
|
||||
{
|
||||
if (a.m_error || b.m_error)
|
||||
{
|
||||
if (b.m_error)
|
||||
return GncRational(0, 1, b.m_error);
|
||||
return GncRational(0, 1, a.m_error);
|
||||
}
|
||||
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.isOverflow() || num.isNan() || den.isOverflow() || den.isNan())
|
||||
return GncRational(0, 1, GNC_ERROR_OVERFLOW);
|
||||
return GncRational(num, den);
|
||||
}
|
||||
|
||||
void
|
||||
GncRational::operator+=(GncRational b)
|
||||
{
|
||||
GncRational new_val = *this + b;
|
||||
*this = std::move(new_val);
|
||||
}
|
||||
|
||||
void
|
||||
GncRational::operator-=(GncRational b)
|
||||
{
|
||||
GncRational new_val = *this - b;
|
||||
*this = std::move(new_val);
|
||||
}
|
||||
|
||||
void
|
||||
GncRational::operator*=(GncRational b)
|
||||
{
|
||||
GncRational new_val = *this * b;
|
||||
*this = std::move(new_val);
|
||||
}
|
||||
|
||||
void
|
||||
GncRational::operator/=(GncRational b)
|
||||
{
|
||||
GncRational new_val = *this / b;
|
||||
*this = std::move(new_val);
|
||||
}
|
||||
|
||||
GncRational&
|
||||
GncRational::mul (const GncRational& b, GncDenom& d) noexcept
|
||||
{
|
||||
if (m_error || b.m_error)
|
||||
{
|
||||
if (b.m_error)
|
||||
m_error = b.m_error;
|
||||
return *this;
|
||||
}
|
||||
m_num *= b.m_num;
|
||||
m_den *= b.m_den;
|
||||
*this *= b;
|
||||
round (d);
|
||||
return *this;
|
||||
}
|
||||
@@ -101,39 +198,7 @@ GncRational::mul (const GncRational& b, GncDenom& d) noexcept
|
||||
GncRational&
|
||||
GncRational::div (GncRational b, GncDenom& d) noexcept
|
||||
{
|
||||
if (m_error || b.m_error)
|
||||
{
|
||||
if (b.m_error)
|
||||
m_error = b.m_error;
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (b.m_num.isNeg())
|
||||
{
|
||||
m_num = -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 (m_den == b.m_den)
|
||||
{
|
||||
m_den = b.m_num;
|
||||
round(d);
|
||||
return *this;
|
||||
}
|
||||
/* Protect against possibly preventable overflow: */
|
||||
if (m_num.isBig() || m_den.isBig() ||
|
||||
b.m_num.isBig() || b.m_den.isBig())
|
||||
{
|
||||
GncInt128 gcd = b.m_den.gcd(m_den);
|
||||
b.m_den /= gcd;
|
||||
m_den /= gcd;
|
||||
}
|
||||
|
||||
m_num *= b.m_den;
|
||||
m_den *= b.m_num;
|
||||
*this /= b;
|
||||
round (d);
|
||||
return *this;
|
||||
}
|
||||
@@ -141,15 +206,7 @@ GncRational::div (GncRational b, GncDenom& d) noexcept
|
||||
GncRational&
|
||||
GncRational::add (const GncRational& b, GncDenom& d) noexcept
|
||||
{
|
||||
if (m_error || b.m_error)
|
||||
{
|
||||
if (b.m_error)
|
||||
m_error = b.m_error;
|
||||
return *this;
|
||||
}
|
||||
GncInt128 lcm = m_den.lcm (b.m_den);
|
||||
m_num = m_num * lcm / m_den + b.m_num * lcm / b.m_den;
|
||||
m_den = lcm;
|
||||
*this += b;
|
||||
round (d);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@ public:
|
||||
GncRational& div(GncRational b, GncDenom& d) noexcept;
|
||||
GncRational& add(const GncRational& b, GncDenom& d) noexcept;
|
||||
GncRational& sub(const GncRational& b, GncDenom& d) noexcept;
|
||||
|
||||
void operator+=(GncRational b);
|
||||
void operator-=(GncRational b);
|
||||
void operator*=(GncRational b);
|
||||
void operator/=(GncRational b);
|
||||
/** Inverts the number, equivalent of /= {1, 1} */
|
||||
GncRational& inv() noexcept;
|
||||
|
||||
@@ -61,6 +64,12 @@ public:
|
||||
GNCNumericErrorCode m_error;
|
||||
};
|
||||
|
||||
GncRational operator+(GncRational a, GncRational b);
|
||||
GncRational operator-(GncRational a, GncRational b);
|
||||
GncRational operator*(GncRational a, GncRational b);
|
||||
GncRational operator/(GncRational a, GncRational b);
|
||||
|
||||
|
||||
/** Encapsulates the rounding specifications computations. */
|
||||
struct GncDenom
|
||||
{
|
||||
|
||||
@@ -67,4 +67,74 @@ TEST(gncrational_constructors, test_with_error_code)
|
||||
EXPECT_EQ(456, value.m_den);
|
||||
EXPECT_EQ(GNC_ERROR_OVERFLOW, value.m_error);
|
||||
}
|
||||
|
||||
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 (GNC_ERROR_OK, c.m_error);
|
||||
a += b;
|
||||
EXPECT_EQ (777778777641976301, a.m_num);
|
||||
EXPECT_EQ (1000000000, a.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, a.m_error);
|
||||
}
|
||||
|
||||
TEST(gncrational_operators, test_subtraction)
|
||||
{
|
||||
GncRational a(123456789987654321, 1000000000);
|
||||
GncRational b(65432198765432198, 100000000);
|
||||
GncRational c = a - b;
|
||||
EXPECT_EQ (530865197666667659, c.m_num);
|
||||
EXPECT_FALSE(c.m_num.isNeg());
|
||||
EXPECT_EQ (1000000000, c.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, c.m_error);
|
||||
c = b - a;
|
||||
EXPECT_EQ (-530865197666667659, c.m_num);
|
||||
EXPECT_TRUE(c.m_num.isNeg());
|
||||
EXPECT_EQ (1000000000, c.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, c.m_error);
|
||||
a -= b;
|
||||
EXPECT_EQ (530865197666667659, a.m_num);
|
||||
EXPECT_FALSE(a.m_num.isNeg());
|
||||
EXPECT_EQ (1000000000, a.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, a.m_error);
|
||||
}
|
||||
|
||||
TEST(gncrational_operators, test_multiplication)
|
||||
{
|
||||
GncRational a(123456789987654321, 1000000000);
|
||||
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);
|
||||
EXPECT_EQ (GNC_ERROR_OK, c.m_error);
|
||||
a *= b;
|
||||
EXPECT_EQ (GncInt128(UINT64_C(437911925765117),
|
||||
UINT64_C(8081008345983448486)), a.m_num);
|
||||
EXPECT_EQ (100000000000000000, a.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, a.m_error);
|
||||
}
|
||||
|
||||
TEST(gncrational_operators, test_division)
|
||||
{
|
||||
GncRational a(123456789987654321, 1000000000);
|
||||
GncRational b(65432198765432198, 100000000);
|
||||
GncRational c = a / b;
|
||||
EXPECT_EQ (GncInt128(UINT64_C(669260), UINT64_C(11059994577585475840)),
|
||||
c.m_num);
|
||||
EXPECT_EQ (GncInt128(UINT64_C(3547086), UINT64_C(11115994079396609024)),
|
||||
c.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, c.m_error);
|
||||
|
||||
a /= b;
|
||||
EXPECT_EQ (GncInt128(UINT64_C(669260), UINT64_C(11059994577585475840)),
|
||||
a.m_num);
|
||||
EXPECT_EQ (GncInt128(UINT64_C(3547086), UINT64_C(11115994079396609024)),
|
||||
a.m_den);
|
||||
EXPECT_EQ (GNC_ERROR_OK, c.m_error);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user