GnuCash  5.6-150-g038405b370+
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 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
Definition: gnc-int128.cpp:247

◆ GncRational() [3/3]

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 }
bool isNeg() const noexcept
Definition: gnc-int128.cpp:247
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 96 of file gnc-rational.cpp.

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

◆ 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 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.
Definition: gnc-int128.cpp:182
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:245

◆ 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 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
Definition: gnc-int128.cpp:253

◆ 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 }
Intermediate result overflow.
Definition: gnc-numeric.h:225
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 80 of file gnc-rational.cpp.

81 {
82  return GncRational(-m_num, m_den);
83 }
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 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 }
GncInt128 gcd(GncInt128 b) const noexcept
Computes the Greatest Common Divisor between the object and parameter.
Definition: gnc-int128.cpp:182
GncInt128 gcd(int64_t a, int64_t b)
Compute the greatest common denominator of two integers.
bool isNan() const noexcept
Definition: gnc-int128.cpp:265
GncRational()
Default constructor provides the zero value.
bool isOverflow() const noexcept
Definition: gnc-int128.cpp:259

◆ 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 }
bool isBig() const noexcept
Definition: gnc-int128.cpp:253
Rational number class using GncInt128 for the numerator and denominator.
bool isZero() const noexcept
Definition: gnc-int128.cpp:277
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:723

◆ 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
Definition: gnc-int128.cpp:271

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