GnuCash c935c2f+
Loading...
Searching...
No Matches
Data Structures | Public Member Functions
GncRational Class Reference

Rational number class using GncInt128 for the numerator and denominator. More...

#include <gnc-rational.hpp>

Public Member Functions

 GncRational ()
 Default constructor provides the zero value.
 
 GncRational (GncInt128 num, GncInt128 den) noexcept
 GncInt128 constructor.
 
 GncRational (gnc_numeric n) noexcept
 Convenience constructor from the C API's gnc_numeric.
 
 GncRational (GncNumeric n) noexcept
 GncNumeric constructor.
 
 GncRational (const GncRational &rhs)=default
 
 GncRational (GncRational &&rhs)=default
 
GncRationaloperator= (const GncRational &rhs)=default
 
GncRationaloperator= (GncRational &&rhs)=default
 
bool valid () const noexcept
 Report if both members are valid numbers.
 
bool is_big () const noexcept
 Report if either numerator or denominator are too big to fit in an int64_t.
 
 operator gnc_numeric () const noexcept
 Conversion operator; use static_cast<gnc_numeric>(foo).
 
GncRational operator- () const noexcept
 Make a new GncRational with the opposite sign.
 
GncRational reduce () const
 Return an equivalent fraction with all common factors between the numerator and the denominator removed.
 
GncRational round_to_numeric () const
 Round to fit an int64_t, finding the closest possible approximation.
 
template<RoundType RT>
GncRational convert (GncInt128 new_denom) const
 Convert a GncRational to use a new denominator.
 
template<RoundType RT>
GncRational convert_sigfigs (unsigned int figs) const
 Convert with the specified sigfigs.
 
GncInt128 num () const noexcept
 Numerator accessor.
 
GncInt128 denom () const noexcept
 Denominator accessor.
 
void operator+= (GncRational b)
 
void operator-= (GncRational b)
 
void operator*= (GncRational b)
 
void operator/= (GncRational b)
 
GncRational inv () const noexcept
 Inverts the number, equivalent of /= {1, 1}.
 
GncRational abs () const noexcept
 Absolute value; return value is always >= 0 and of same magnitude.
 
int cmp (GncRational b)
 Compare function.
 
int cmp (GncInt128 b)
 

Detailed Description

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:

Definition at line 57 of file gnc-rational.hpp.

Constructor & Destructor Documentation

◆ GncRational() [1/4]

GncRational::GncRational ( )
inline

Default constructor provides the zero value.

Definition at line 63 of file gnc-rational.hpp.

63: m_num(0), m_den(1) {}

◆ GncRational() [2/4]

GncRational::GncRational ( GncInt128  num,
GncInt128  den 
)
inlinenoexcept

GncInt128 constructor.

This will take any flavor of built-in integer thanks to implicit construction of the GncInt128s.

Definition at line 68 of file gnc-rational.hpp.

69 : m_num(num), m_den(den) {}
GncInt128 num() const noexcept
Numerator accessor.

◆ GncRational() [3/4]

GncRational::GncRational ( gnc_numeric  n)
noexcept

Convenience constructor from the C API's gnc_numeric.

Definition at line 39 of file gnc-rational.cpp.

39 :
40 m_num (n.num), m_den (n.denom)
41{
42 if (m_den.isNeg())
43 {
44 m_num *= -m_den;
45 m_den = 1;
46 }
47}
bool isNeg() const noexcept

◆ GncRational() [4/4]

GncRational::GncRational ( GncNumeric  n)
noexcept

GncNumeric constructor.

Definition at line 29 of file gnc-rational.cpp.

29 :
30 m_num(n.num()), m_den(n.denom())
31{
32 if (m_den.isNeg())
33 {
34 m_num *= -m_den;
35 m_den = 1;
36 }
37}
int64_t denom() const noexcept
Accessor for denominator value.
int64_t num() const noexcept
Accessor for numerator value.

Member Function Documentation

◆ abs()

GncRational GncRational::abs ( ) const
noexcept

Absolute value; return value is always >= 0 and of same magnitude.

Definition at line 96 of file gnc-rational.cpp.

97{
98 if (m_num < 0)
99 return -*this;
100 return *this;
101}

◆ cmp() [1/2]

int GncRational::cmp ( GncInt128  b)
inline

Definition at line 175 of file gnc-rational.hpp.

175{ return cmp(GncRational(b, 1)); }
GncRational()
Default constructor provides the zero value.
int cmp(GncRational b)
Compare function.

◆ cmp() [2/2]

int GncRational::cmp ( GncRational  b)

Compare function.

Parameters
bGncNumeric or integer value to compare to.
Returns
-1 if < b, 0 if equal, 1 if > b.

Definition at line 132 of file gnc-rational.cpp.

133{
134 if (m_den == b.denom())
135 {
136 auto b_num = b.num();
137 return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
138 }
139 auto gcd = m_den.gcd(b.denom());
140 GncInt128 a_num(m_num * b.denom() / gcd), b_num(b.num() * m_den / gcd);
141 return a_num < b_num ? -1 : b_num < a_num ? 1 : 0;
142}
GncInt128 gcd(GncInt128 b) const noexcept
Computes the Greatest Common Divisor between the object and parameter.
GncInt128 denom() const noexcept
Denominator accessor.
GncInt128 gcd(int64_t a, int64_t b)
Compute the greatest common denominator of two integers.

◆ convert()

template<RoundType RT>
GncRational GncRational::convert ( GncInt128  new_denom) const
inline

Convert a GncRational to use a new denominator.

If rounding is necessary use the indicated template specification. For example, to use half-up rounding you'd call bar = foo.convert<RoundType::half_up>(1000). If you specify RoundType::never this will throw std::domain_error if rounding is required.

Parameters
new_denomThe new denominator to convert the fraction to.
Returns
A new GncRational having the requested denominator.

Definition at line 117 of file gnc-rational.hpp.

118 {
119 auto params = prepare_conversion(new_denom);
120 if (new_denom == GNC_DENOM_AUTO)
121 new_denom = m_den;
122 if (params.rem == 0)
123 return GncRational(params.num, new_denom);
124 return GncRational(round(params.num, params.den,
125 params.rem, RT2T<RT>()), new_denom);
126 }
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.

◆ convert_sigfigs()

template<RoundType RT>
GncRational GncRational::convert_sigfigs ( unsigned int  figs) const
inline

Convert with the specified sigfigs.

The resulting denominator depends on the value of the GncRational, such that the specified significant digits are retained in the numerator and the denominator is always a power of

  1. This is of rather dubious benefit in an accounting program, but it's used in several places so it needs to be implemented.
Parameters
figsThe number of digits to use for the numerator.
Returns
A GncRational with the specified number of digits in the numerator and the appropriate power-of-ten denominator.

Definition at line 140 of file gnc-rational.hpp.

141 {
142 auto new_denom(sigfigs_denom(figs));
143 auto params = prepare_conversion(new_denom);
144 if (new_denom == 0) //It had better not, but just in case...
145 new_denom = 1;
146 if (params.rem == 0)
147 return GncRational(params.num, new_denom);
148 return GncRational(round(params.num, params.den,
149 params.rem, RT2T<RT>()), new_denom);
150 }

◆ denom()

GncInt128 GncRational::denom ( ) const
inlinenoexcept

Denominator accessor.

Definition at line 155 of file gnc-rational.hpp.

155{ return m_den; }

◆ inv()

GncRational GncRational::inv ( ) const
noexcept

Inverts the number, equivalent of /= {1, 1}.

Definition at line 86 of file gnc-rational.cpp.

87{
88 if (m_num == 0)
89 return *this;
90 if (m_num < 0)
91 return GncRational(-m_den, -m_num);
92 return GncRational(m_den, m_num);
93}

◆ is_big()

bool GncRational::is_big ( ) const
noexcept

Report if either numerator or denominator are too big to fit in an int64_t.

Returns
true if either is too big.

Definition at line 58 of file gnc-rational.cpp.

59{
60 if (m_num.isBig() || m_den.isBig())
61 return true;
62 return false;
63}
bool isBig() const noexcept

◆ num()

GncInt128 GncRational::num ( ) const
inlinenoexcept

Numerator accessor.

Definition at line 153 of file gnc-rational.hpp.

153{ return m_num; }

◆ operator gnc_numeric()

GncRational::operator gnc_numeric ( ) const
noexcept

Conversion operator; use static_cast<gnc_numeric>(foo).

Definition at line 65 of file gnc-rational.cpp.

66{
67 if (!valid())
69 try
70 {
71 return {static_cast<int64_t>(m_num), static_cast<int64_t>(m_den)};
72 }
73 catch (std::overflow_error&)
74 {
76 }
77}
bool valid() const noexcept
Report if both members are valid numbers.
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
@ GNC_ERROR_OVERFLOW
Intermediate result overflow.

◆ operator-()

GncRational GncRational::operator- ( ) const
noexcept

Make a new GncRational with the opposite sign.

Definition at line 80 of file gnc-rational.cpp.

81{
82 return GncRational(-m_num, m_den);
83}

◆ reduce()

GncRational GncRational::reduce ( ) const

Return an equivalent fraction with all common factors between the numerator and the denominator removed.

Returns
reduced GncRational

Definition at line 181 of file gnc-rational.cpp.

182{
183 auto gcd = m_den.gcd(m_num);
184 if (gcd.isNan() || gcd.isOverflow())
185 throw std::overflow_error("Reduce failed, calculation of gcd overflowed.");
186 return GncRational(m_num / gcd, m_den / gcd);
187}
bool isOverflow() const noexcept
bool isNan() const noexcept

◆ round_to_numeric()

GncRational GncRational::round_to_numeric ( ) const

Round to fit an int64_t, finding the closest possible approximation.

Throws std::overflow_error if m_den is 1 and m_num is big.

Returns
rounded GncRational

Definition at line 190 of file gnc-rational.cpp.

191{
192 unsigned int ll_bits = GncInt128::legbits;
193 if (m_num.isZero())
194 return GncRational(); //Default constructor makes 0/1
195 if (!(m_num.isBig() || m_den.isBig()))
196 return *this;
197 if (m_num.abs() > m_den)
198 {
199 auto quot(m_num / m_den);
200 if (quot.isBig())
201 {
202 std::ostringstream msg;
203 msg << " Cannot be represented as a "
204 << "GncNumeric. Its integer value is too large.\n";
205 throw std::overflow_error(msg.str());
206 }
207 GncRational new_v;
208 while (new_v.num().isZero())
209 {
210 try
211 {
212 new_v = convert<RoundType::half_down>(m_den / (m_num.abs() >> ll_bits));
213 if (new_v.is_big())
214 {
215 --ll_bits;
216 new_v = GncRational();
217 }
218 }
219 catch(const std::overflow_error& err)
220 {
221 --ll_bits;
222 }
223 }
224 return new_v;
225 }
226 auto quot(m_den / m_num);
227 if (quot.isBig())
228 return GncRational(); //Smaller than can be represented as a GncNumeric
229 GncRational new_v;
230 while (new_v.num().isZero())
231 {
232 auto divisor = m_den >> ll_bits;
233 if (m_num.isBig())
234 {
235 GncInt128 oldnum(m_num), num, rem;
236 oldnum.div(divisor, num, rem);
237 auto den = m_den / divisor;
238 num += rem * 2 >= den ? 1 : 0;
239 if (num.isBig() || den.isBig())
240 {
241 --ll_bits;
242 continue;
243 }
244 GncRational new_rational(num, den);
245 return new_rational;
246 }
247 new_v = convert<RoundType::half_down>(m_den / divisor);
248 if (new_v.is_big())
249 {
250 --ll_bits;
251 new_v = GncRational();
252 }
253 }
254 return new_v;
255}
void div(const GncInt128 &d, GncInt128 &q, GncInt128 &r) const noexcept
Computes a quotient and a remainder, passed as reference parameters.
bool isZero() const noexcept
Rational number class using GncInt128 for the numerator and denominator.
bool is_big() const noexcept
Report if either numerator or denominator are too big to fit in an int64_t.

◆ valid()

bool GncRational::valid ( ) const
noexcept

Report if both members are valid numbers.

Returns
true if neither numerator nor denominator are Nan or Overflowed.

Definition at line 50 of file gnc-rational.cpp.

51{
52 if (m_num.valid() && m_den.valid())
53 return true;
54 return false;
55}
bool valid() const noexcept

The documentation for this class was generated from the following files: