GnuCash  4.12-68-g5dc52459a1+
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. More...
 
 GncRational (gnc_numeric n) noexcept
 Convenience constructor from the C API's gnc_numeric. More...
 
 GncRational (GncNumeric n) noexcept
 GncNumeric constructor. More...
 
 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. More...
 
bool is_big () const noexcept
 Report if either numerator or denominator are too big to fit in an int64_t. More...
 
 operator gnc_numeric () const noexcept
 Conversion operator; use static_cast<gnc_numeric>(foo). More...
 
GncRational operator- () const noexcept
 Make a new GncRational with the opposite sign. More...
 
GncRational reduce () const
 Return an equivalent fraction with all common factors between the numerator and the denominator removed. More...
 
GncRational round_to_numeric () const
 Round to fit an int64_t, finding the closest possible approximation. More...
 
template<RoundType RT>
GncRational convert (GncInt128 new_denom) const
 Convert a GncRational to use a new denominator. More...
 
template<RoundType RT>
GncRational convert_sigfigs (unsigned int figs) const
 Convert with the specified sigfigs. More...
 
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. More...
 
int cmp (GncRational b)
 Compare function. More...
 
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/3]

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() [2/3]

GncRational::GncRational ( gnc_numeric  n)
noexcept

Convenience constructor from the C API's gnc_numeric.

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

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

◆ GncRational() [3/3]

GncRational::GncRational ( GncNumeric  n)
noexcept

GncNumeric constructor.

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

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

Member Function Documentation

◆ abs()

GncRational GncRational::abs ( ) const
noexcept

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

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

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

◆ cmp()

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 131 of file gnc-rational.cpp.

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

◆ 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  }
GncRational()
Default constructor provides the zero value.
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:246

◆ 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  }
GncRational()
Default constructor provides the zero value.

◆ 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 57 of file gnc-rational.cpp.

58 {
59  if (m_num.isBig() || m_den.isBig())
60  return true;
61  return false;
62 }
bool isBig() const noexcept
Definition: gnc-int128.cpp:256

◆ operator gnc_numeric()

GncRational::operator gnc_numeric ( ) const
noexcept

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

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

65 {
66  if (!valid())
68  try
69  {
70  return {static_cast<int64_t>(m_num), static_cast<int64_t>(m_den)};
71  }
72  catch (std::overflow_error&)
73  {
75  }
76 }
Intermediate result overflow.
Definition: gnc-numeric.h:226
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...
bool valid() const noexcept
Report if both members are valid numbers.

◆ operator-()

GncRational GncRational::operator- ( ) const
noexcept

Make a new GncRational with the opposite sign.

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

80 {
81  return GncRational(-m_num, m_den);
82 }
GncRational()
Default constructor provides the zero value.

◆ 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 180 of file gnc-rational.cpp.

181 {
182  auto gcd = m_den.gcd(m_num);
183  if (gcd.isNan() || gcd.isOverflow())
184  throw std::overflow_error("Reduce failed, calculation of gcd overflowed.");
185  return GncRational(m_num / gcd, m_den / gcd);
186 }
GncInt128 gcd(GncInt128 b) const noexcept
Computes the Greatest Common Divisor between the object and parameter.
Definition: gnc-int128.cpp:185
GncInt128 gcd(int64_t a, int64_t b)
Compute the greatest common denominator of two integers.
bool isNan() const noexcept
Definition: gnc-int128.cpp:268
GncRational()
Default constructor provides the zero value.
bool isOverflow() const noexcept
Definition: gnc-int128.cpp:262

◆ 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 189 of file gnc-rational.cpp.

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

◆ 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 49 of file gnc-rational.cpp.

50 {
51  if (m_num.valid() && m_den.valid())
52  return true;
53  return false;
54 }
bool valid() const noexcept
Definition: gnc-int128.cpp:274

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