mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Change signature of GncRational::round from taking a GncDenominator…
to a separate denominator and RoundType.
This commit is contained in:
parent
6f5d628b12
commit
3975b0b465
@ -34,6 +34,7 @@ extern "C"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef __cplusplus
|
||||
#include "qof.h"
|
||||
}
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
@ -262,8 +263,14 @@ gnc_numeric_add(gnc_numeric a, gnc_numeric b,
|
||||
if (new_denom.m_error)
|
||||
return gnc_numeric_error (new_denom.m_error);
|
||||
|
||||
|
||||
return static_cast<gnc_numeric>(an.add(bn, new_denom));
|
||||
try
|
||||
{
|
||||
return static_cast<gnc_numeric>(an.add(bn, new_denom));
|
||||
}
|
||||
catch (const std::overflow_error& err)
|
||||
{
|
||||
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
/* *******************************************************************
|
||||
@ -302,8 +309,15 @@ gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
|
||||
GncDenom new_denom (an, bn, denom, how);
|
||||
if (new_denom.m_error)
|
||||
return gnc_numeric_error (new_denom.m_error);
|
||||
try
|
||||
{
|
||||
return static_cast<gnc_numeric>(an.mul(bn, new_denom));
|
||||
}
|
||||
catch (const std::overflow_error& err)
|
||||
{
|
||||
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
|
||||
}
|
||||
|
||||
return static_cast<gnc_numeric>(an.mul(bn, new_denom));
|
||||
}
|
||||
|
||||
|
||||
@ -324,8 +338,14 @@ gnc_numeric_div(gnc_numeric a, gnc_numeric b,
|
||||
GncDenom new_denom (an, bn, denom, how);
|
||||
if (new_denom.m_error)
|
||||
return gnc_numeric_error (new_denom.m_error);
|
||||
|
||||
return static_cast<gnc_numeric>(an.div(bn, new_denom));
|
||||
try
|
||||
{
|
||||
return static_cast<gnc_numeric>(an.div(bn, new_denom));
|
||||
}
|
||||
catch (const std::overflow_error& err)
|
||||
{
|
||||
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
/* *******************************************************************
|
||||
@ -368,8 +388,16 @@ gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
|
||||
{
|
||||
GncNumeric a (in), b (gnc_numeric_zero());
|
||||
GncDenom d (a, b, denom, how);
|
||||
a.round (d);
|
||||
return static_cast<gnc_numeric>(a);
|
||||
try
|
||||
{
|
||||
d.reduce(a);
|
||||
a.round (d.get(), d.m_round);
|
||||
return static_cast<gnc_numeric>(a);
|
||||
}
|
||||
catch (const std::overflow_error& err)
|
||||
{
|
||||
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -391,8 +419,16 @@ gnc_numeric_reduce(gnc_numeric in)
|
||||
return in;
|
||||
GncNumeric a (in), b (gnc_numeric_zero());
|
||||
GncDenom d (a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
||||
a.round (d);
|
||||
return static_cast<gnc_numeric>(a);
|
||||
try
|
||||
{
|
||||
d.reduce(a);
|
||||
a.round (d.get(), d.m_round);
|
||||
return static_cast<gnc_numeric>(a);
|
||||
}
|
||||
catch (const std::overflow_error& err)
|
||||
{
|
||||
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -189,45 +189,41 @@ GncRational::operator/=(GncRational b)
|
||||
}
|
||||
|
||||
GncRational&
|
||||
GncRational::mul (const GncRational& b, GncDenom& d) noexcept
|
||||
GncRational::mul (const GncRational& b, GncDenom& d)
|
||||
{
|
||||
*this *= b;
|
||||
round (d);
|
||||
d.reduce(*this);
|
||||
round (d.get(), d.m_round);
|
||||
return *this;
|
||||
}
|
||||
|
||||
GncRational&
|
||||
GncRational::div (GncRational b, GncDenom& d) noexcept
|
||||
GncRational::div (GncRational b, GncDenom& d)
|
||||
{
|
||||
*this /= b;
|
||||
round (d);
|
||||
d.reduce(*this);
|
||||
round (d.get(), d.m_round);
|
||||
return *this;
|
||||
}
|
||||
|
||||
GncRational&
|
||||
GncRational::add (const GncRational& b, GncDenom& d) noexcept
|
||||
GncRational::add (const GncRational& b, GncDenom& d)
|
||||
{
|
||||
*this += b;
|
||||
round (d);
|
||||
d.reduce(*this);
|
||||
round (d.get(), d.m_round);
|
||||
return *this;
|
||||
}
|
||||
|
||||
GncRational&
|
||||
GncRational::sub (const GncRational& b, GncDenom& d) noexcept
|
||||
GncRational::sub (const GncRational& b, GncDenom& d)
|
||||
{
|
||||
return add(-b, d);
|
||||
}
|
||||
|
||||
void
|
||||
GncRational::round (GncDenom& denom) noexcept
|
||||
GncRational::round (GncInt128 new_den, RoundType rtype)
|
||||
{
|
||||
denom.reduce (*this);
|
||||
if (m_error == GNC_ERROR_OK && denom.m_error != GNC_ERROR_OK)
|
||||
{
|
||||
m_error = denom.m_error;
|
||||
return;
|
||||
}
|
||||
GncInt128 new_den = denom.get();
|
||||
if (new_den == 0) new_den = m_den;
|
||||
if (!(m_num.isBig() || new_den.isBig() ))
|
||||
{
|
||||
@ -243,9 +239,20 @@ GncRational::round (GncDenom& denom) noexcept
|
||||
GncInt128 new_num {}, remainder {};
|
||||
if (new_den.isNeg())
|
||||
m_num.div(-new_den * m_den, new_num, remainder);
|
||||
else
|
||||
else if (new_den != m_den)
|
||||
(m_num * new_den).div(m_den, new_num, remainder);
|
||||
|
||||
else
|
||||
{
|
||||
new_num = m_num;
|
||||
new_den = m_den;
|
||||
remainder = 0;
|
||||
}
|
||||
if (new_num.isOverflow() || new_den.isOverflow() || remainder.isOverflow())
|
||||
throw std::overflow_error("Overflow during rounding.");
|
||||
if (new_num.isNan() || new_den.isNan() || remainder.isNan())
|
||||
{
|
||||
throw std::underflow_error("Underflow during rounding.");
|
||||
}
|
||||
if (remainder.isZero() && !(new_num.isBig() || new_den.isBig()))
|
||||
{
|
||||
m_num = new_num;
|
||||
@ -255,49 +262,52 @@ GncRational::round (GncDenom& denom) noexcept
|
||||
|
||||
if (new_num.isBig() || new_den.isBig())
|
||||
{
|
||||
if (!denom.m_auto)
|
||||
{
|
||||
m_error = GNC_ERROR_OVERFLOW;
|
||||
return;
|
||||
}
|
||||
|
||||
/* First, try to reduce it */
|
||||
GncInt128 gcd = new_num.gcd(new_den);
|
||||
if (!(gcd.isNan() || gcd.isOverflow()))
|
||||
{
|
||||
new_num /= gcd;
|
||||
new_den /= gcd;
|
||||
remainder /= gcd;
|
||||
}
|
||||
|
||||
/* if that didn't work, shift both num and den down until neither is "big", th
|
||||
/* if that didn't work, shift both num and den down until neither is "big", then
|
||||
* fall through to rounding.
|
||||
*/
|
||||
while (new_num && new_num.isBig() && new_den && new_den.isBig())
|
||||
while (rtype != RoundType::never && new_num && new_num.isBig() &&
|
||||
new_den && new_den.isBig())
|
||||
{
|
||||
new_num >>= 1;
|
||||
new_den >>= 1;
|
||||
remainder >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (remainder == 0)
|
||||
{
|
||||
m_num = new_num;
|
||||
m_den = new_den;
|
||||
return;
|
||||
}
|
||||
/* If we got here, then we can't exactly represent the rational with
|
||||
* new_denom. We must either round or punt.
|
||||
*/
|
||||
switch (denom.m_round)
|
||||
switch (rtype)
|
||||
{
|
||||
case GncDenom::RoundType::never:
|
||||
case RoundType::never:
|
||||
m_error = GNC_ERROR_REMAINDER;
|
||||
return;
|
||||
case GncDenom::RoundType::floor:
|
||||
case RoundType::floor:
|
||||
if (new_num.isNeg()) ++new_num;
|
||||
break;
|
||||
case GncDenom::RoundType::ceiling:
|
||||
case RoundType::ceiling:
|
||||
if (! new_num.isNeg()) ++new_num;
|
||||
break;
|
||||
case GncDenom::RoundType::truncate:
|
||||
case RoundType::truncate:
|
||||
break;
|
||||
case GncDenom::RoundType::promote:
|
||||
case RoundType::promote:
|
||||
new_num += new_num.isNeg() ? -1 : 1;
|
||||
break;
|
||||
case GncDenom::RoundType::half_down:
|
||||
case RoundType::half_down:
|
||||
if (new_den.isNeg())
|
||||
{
|
||||
if (remainder * 2 > m_den * new_den)
|
||||
@ -306,7 +316,7 @@ GncRational::round (GncDenom& denom) noexcept
|
||||
else if (remainder * 2 > m_den)
|
||||
new_num += new_num.isNeg() ? -1 : 1;
|
||||
break;
|
||||
case GncDenom::RoundType::half_up:
|
||||
case RoundType::half_up:
|
||||
if (new_den.isNeg())
|
||||
{
|
||||
if (remainder * 2 >= m_den * new_den)
|
||||
@ -315,7 +325,7 @@ GncRational::round (GncDenom& denom) noexcept
|
||||
else if (remainder * 2 >= m_den)
|
||||
new_num += new_num.isNeg() ? -1 : 1;
|
||||
break;
|
||||
case GncDenom::RoundType::bankers:
|
||||
case RoundType::bankers:
|
||||
if (new_den.isNeg())
|
||||
{
|
||||
if (remainder * 2 > m_den * -new_den ||
|
||||
@ -366,7 +376,7 @@ GncRational::round_to_numeric() const
|
||||
auto divisor = static_cast<int64_t>(m_den / (m_num.abs() >> 62));
|
||||
GncDenom gnc_denom(new_rational, scratch, divisor,
|
||||
GNC_HOW_RND_ROUND_HALF_DOWN);
|
||||
new_rational.round(gnc_denom);
|
||||
new_rational.round(gnc_denom.get(), gnc_denom.m_round);
|
||||
return new_rational;
|
||||
}
|
||||
auto quot(m_den / m_num);
|
||||
@ -387,15 +397,15 @@ GncRational::round_to_numeric() const
|
||||
auto int_div = static_cast<int64_t>(m_den / divisor);
|
||||
GncDenom gnc_denom(new_rational, scratch, int_div,
|
||||
GNC_HOW_RND_ROUND_HALF_DOWN);
|
||||
new_rational.round(gnc_denom);
|
||||
new_rational.round(gnc_denom.get(), gnc_denom.m_round);
|
||||
return new_rational;
|
||||
}
|
||||
|
||||
GncDenom::GncDenom (GncRational& a, GncRational& b,
|
||||
int64_t spec, unsigned int how) noexcept :
|
||||
m_value (spec),
|
||||
m_round (static_cast<GncDenom::RoundType>(how & GNC_NUMERIC_RND_MASK)),
|
||||
m_type (static_cast<GncDenom::DenomType>(how & GNC_NUMERIC_DENOM_MASK)),
|
||||
m_round (static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK)),
|
||||
m_type (static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK)),
|
||||
m_auto (spec == GNC_DENOM_AUTO),
|
||||
m_sigfigs ((how & GNC_NUMERIC_SIGFIGS_MASK) >> 8),
|
||||
m_error (GNC_ERROR_OK)
|
||||
|
@ -28,6 +28,28 @@
|
||||
|
||||
struct GncDenom;
|
||||
|
||||
enum class RoundType
|
||||
{
|
||||
floor = GNC_HOW_RND_FLOOR,
|
||||
ceiling = GNC_HOW_RND_CEIL,
|
||||
truncate = GNC_HOW_RND_TRUNC,
|
||||
promote = GNC_HOW_RND_PROMOTE,
|
||||
half_down = GNC_HOW_RND_ROUND_HALF_DOWN,
|
||||
half_up = GNC_HOW_RND_ROUND_HALF_UP,
|
||||
bankers = GNC_HOW_RND_ROUND,
|
||||
never = GNC_HOW_RND_NEVER,
|
||||
};
|
||||
|
||||
enum class DenomType
|
||||
{
|
||||
den_auto = GNC_DENOM_AUTO,
|
||||
exact = GNC_HOW_DENOM_EXACT,
|
||||
reduce = GNC_HOW_DENOM_REDUCE,
|
||||
lcd = GNC_HOW_DENOM_LCD,
|
||||
fixed = GNC_HOW_DENOM_FIXED,
|
||||
sigfigs = GNC_HOW_DENOM_SIGFIG,
|
||||
};
|
||||
|
||||
/** @ingroup QOF
|
||||
* @brief Rational number class using GncInt128 for the numerator and denominator.
|
||||
*/
|
||||
@ -62,14 +84,14 @@ public:
|
||||
/** Round/convert this to the denominator provided by d, according to d's
|
||||
* m_round value.
|
||||
*/
|
||||
void round (GncDenom& d) noexcept;
|
||||
/* These are mutators; in other words, they implement the equivalent of
|
||||
* operators *=, /=, +=, and -=. They return a reference to this for chaining.
|
||||
*/
|
||||
GncRational& mul(const GncRational& b, GncDenom& d) noexcept;
|
||||
GncRational& div(GncRational b, GncDenom& d) noexcept;
|
||||
GncRational& add(const GncRational& b, GncDenom& d) noexcept;
|
||||
GncRational& sub(const GncRational& b, GncDenom& d) noexcept;
|
||||
GncRational& mul(const GncRational& b, GncDenom& d);
|
||||
GncRational& div(GncRational b, GncDenom& d);
|
||||
GncRational& add(const GncRational& b, GncDenom& d);
|
||||
GncRational& sub(const GncRational& b, GncDenom& d);
|
||||
void round (GncInt128 new_den, RoundType rtype);
|
||||
void operator+=(GncRational b);
|
||||
void operator-=(GncRational b);
|
||||
void operator*=(GncRational b);
|
||||
@ -95,26 +117,6 @@ struct GncDenom
|
||||
void reduce (const GncRational& a) noexcept;
|
||||
GncInt128 get () const noexcept { return m_value; }
|
||||
|
||||
enum class RoundType : int
|
||||
{
|
||||
floor = GNC_HOW_RND_FLOOR,
|
||||
ceiling = GNC_HOW_RND_CEIL,
|
||||
truncate = GNC_HOW_RND_TRUNC,
|
||||
promote = GNC_HOW_RND_PROMOTE,
|
||||
half_down = GNC_HOW_RND_ROUND_HALF_DOWN,
|
||||
half_up = GNC_HOW_RND_ROUND_HALF_UP,
|
||||
bankers = GNC_HOW_RND_ROUND,
|
||||
never = GNC_HOW_RND_NEVER,
|
||||
};
|
||||
enum class DenomType : int
|
||||
{
|
||||
exact = GNC_HOW_DENOM_EXACT,
|
||||
reduce = GNC_HOW_DENOM_REDUCE,
|
||||
lcd = GNC_HOW_DENOM_LCD,
|
||||
fixed = GNC_HOW_DENOM_FIXED,
|
||||
sigfigs = GNC_HOW_DENOM_SIGFIG,
|
||||
};
|
||||
|
||||
GncInt128 m_value;
|
||||
RoundType m_round;
|
||||
DenomType m_type;
|
||||
|
@ -792,7 +792,7 @@ check_mult_div (void)
|
||||
* the overflow is eliminated.
|
||||
*/
|
||||
|
||||
check_binary_op (gnc_numeric_error (GNC_ERROR_REMAINDER),
|
||||
check_binary_op (gnc_numeric_error (GNC_ERROR_OVERFLOW),
|
||||
gnc_numeric_div(a, b, GNC_DENOM_AUTO,
|
||||
GNC_HOW_RND_NEVER | GNC_HOW_DENOM_EXACT),
|
||||
a, b, "expected %s got %s = %s / %s for div exact");
|
||||
|
Loading…
Reference in New Issue
Block a user