Skip to content

Commit

Permalink
Implement basic arithmetic operators for GncRational.
Browse files Browse the repository at this point in the history
The operators do no rounding or reducing yet.
  • Loading branch information
jralls committed Feb 20, 2017
1 parent d9aa5e1 commit a852dfb
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 37 deletions.
129 changes: 93 additions & 36 deletions src/libqof/qof/gnc-rational.cpp
Expand Up @@ -83,73 +83,130 @@ GncRational::inv () noexcept
return *this;
}

GncRational&
GncRational::mul (const GncRational& b, GncDenom& d) noexcept
GncRational
operator+(GncRational a, GncRational b)
{
if (m_error || b.m_error)
if (a.m_error || b.m_error)
{
if (b.m_error)
m_error = b.m_error;
return *this;
return GncRational(0, 1, b.m_error);
return GncRational(0, 1, a.m_error);
}
m_num *= b.m_num;
m_den *= b.m_den;
round (d);
return *this;
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&
GncRational::div (GncRational b, GncDenom& d) noexcept
GncRational
operator-(GncRational a, GncRational b)
{
GncRational retval = -a + b;
return retval;
}

GncRational
operator*(GncRational a, GncRational b)
{
if (m_error || b.m_error)
if (a.m_error || b.m_error)
{
if (b.m_error)
m_error = b.m_error;
return *this;
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;
}

if (b.m_num.isNeg())
GncRational
operator/(GncRational a, GncRational b)
{
if (a.m_error || b.m_error)
{
m_num = -m_num;
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 (m_den == b.m_den)
{
m_den = b.m_num;
round(d);
return *this;
}
if (a.m_den == b.m_den)
return GncRational(a.m_num, b.m_num);

/* Protect against possibly preventable overflow: */
if (m_num.isBig() || m_den.isBig() ||
if (a.m_num.isBig() || a.m_den.isBig() ||
b.m_num.isBig() || b.m_den.isBig())
{
GncInt128 gcd = b.m_den.gcd(m_den);
GncInt128 gcd = b.m_den.gcd(a.m_den);
b.m_den /= gcd;
m_den /= gcd;
a.m_den /= gcd;
}

m_num *= b.m_den;
m_den *= b.m_num;
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
{
*this *= b;
round (d);
return *this;
}

GncRational&
GncRational::div (GncRational b, GncDenom& d) noexcept
{
*this /= b;
round (d);
return *this;
}

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;
}
Expand Down
11 changes: 10 additions & 1 deletion src/libqof/qof/gnc-rational.hpp
Expand Up @@ -52,7 +52,10 @@ class GncRational
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;

Expand All @@ -61,6 +64,12 @@ class GncRational
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
{
Expand Down
70 changes: 70 additions & 0 deletions src/libqof/qof/test/gtest-gnc-rational.cpp
Expand Up @@ -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);

}

0 comments on commit a852dfb

Please sign in to comment.