GnuCash  5.6-150-g038405b370+
gnc-numeric.hpp
1 /********************************************************************
2  * gnc-numeric.hpp - A rational number library for int64 *
3  * Copyright 2017 John Ralls <jralls@ceridwen.us> *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20  * *
21  *******************************************************************/
22 
23 #ifndef __GNC_NUMERIC_HPP__
24 #define __GNC_NUMERIC_HPP__
25 
26 #include <string>
27 #include <iostream>
28 #include <locale>
29 #include <typeinfo> // For std::bad_cast exception
30 #include "gnc-rational-rounding.hpp"
31 
32 class GncRational;
33 
60 {
61 public:
65  GncNumeric() : m_num (0), m_den(1) {}
76  GncNumeric(int64_t num, int64_t denom) :
77  m_num(num), m_den(denom) {
78  if (denom == 0)
79  throw std::invalid_argument("Attempt to construct a GncNumeric with a 0 denominator.");
80  }
96  GncNumeric(gnc_numeric in) : m_num(in.num), m_den(in.denom)
97  {
98  if (in.denom == 0)
99  throw std::invalid_argument("Attempt to construct a GncNumeric with a 0 denominator.");
100  /* gnc_numeric has a dumb convention that a negative denominator means
101  * to multiply the numerator by the denominator instead of dividing.
102  */
103  if (in.denom < 0)
104  {
105  m_num *= -in.denom;
106  m_den = 1;
107  }
108  }
117  GncNumeric(double d);
118 
139  GncNumeric(const std::string& str, bool autoround=false);
140  GncNumeric(const GncNumeric& rhs) = default;
141  GncNumeric(GncNumeric&& rhs) = default;
142  GncNumeric& operator=(const GncNumeric& rhs) = default;
143  GncNumeric& operator=(GncNumeric&& rhs) = default;
144  ~GncNumeric() = default;
148  operator gnc_numeric() const noexcept;
152  operator double() const noexcept;
153 
157  int64_t num() const noexcept { return m_num; }
161  int64_t denom() const noexcept { return m_den; }
165  GncNumeric operator-() const noexcept;
169  GncNumeric inv() const noexcept;
173  GncNumeric abs() const noexcept;
180  GncNumeric reduce() const noexcept;
191  template <RoundType RT>
192  GncNumeric convert(int64_t new_denom) const
193  {
194  auto params = prepare_conversion(new_denom);
195  if (new_denom == GNC_DENOM_AUTO)
196  new_denom = m_den;
197  if (params.rem == 0)
198  return GncNumeric(params.num, new_denom);
199  return GncNumeric(round(params.num, params.den,
200  params.rem, RT2T<RT>()), new_denom);
201  }
202 
214  template <RoundType RT>
215  GncNumeric convert_sigfigs(unsigned int figs) const
216  {
217  auto new_denom(sigfigs_denom(figs));
218  auto params = prepare_conversion(new_denom);
219  if (new_denom == 0) //It had better not, but just in case...
220  new_denom = 1;
221  if (params.rem == 0)
222  return GncNumeric(params.num, new_denom);
223  return GncNumeric(round(params.num, params.den,
224  params.rem, RT2T<RT>()), new_denom);
225  }
230  std::string to_string() const noexcept;
234  bool is_decimal() const noexcept;
243  GncNumeric to_decimal(unsigned int max_places=17) const;
253  void operator+=(GncNumeric b);
254  void operator-=(GncNumeric b);
255  void operator*=(GncNumeric b);
256  void operator/=(GncNumeric b);
257  /* @} */
264  int cmp(GncNumeric b);
265  int cmp(int64_t b) { return cmp(GncNumeric(b, 1)); }
267 private:
268  struct round_param
269  {
270  int64_t num;
271  int64_t den;
272  int64_t rem;
273  };
274  /* Calculates the denominator required to convert to figs sigfigs. */
275  int64_t sigfigs_denom(unsigned figs) const noexcept;
276  /* Calculates a round_param struct to pass to a rounding function that will
277  * finish computing a GncNumeric with the new denominator.
278  */
279  round_param prepare_conversion(int64_t new_denom) const;
280  int64_t m_num;
281  int64_t m_den;
282 };
283 
299 GncNumeric operator+(GncNumeric a, GncNumeric b);
300 inline GncNumeric operator+(GncNumeric a, int64_t b)
301 {
302  return a + GncNumeric(b, 1);
303 }
304 inline GncNumeric operator+(int64_t a, GncNumeric b)
305 {
306  return b + GncNumeric(a, 1);
307 }
308 GncNumeric operator-(GncNumeric a, GncNumeric b);
309 inline GncNumeric operator-(GncNumeric a, int64_t b)
310 {
311  return a - GncNumeric(b, 1);
312 }
313 inline GncNumeric operator-(int64_t a, GncNumeric b)
314 {
315  return b - GncNumeric(a, 1);
316 }
317 GncNumeric operator*(GncNumeric a, GncNumeric b);
318 inline GncNumeric operator*(GncNumeric a, int64_t b)
319 {
320  return a * GncNumeric(b, 1);
321 }
322 inline GncNumeric operator*(int64_t a, GncNumeric b)
323 {
324  return b * GncNumeric(a, 1);
325 }
326 GncNumeric operator/(GncNumeric a, GncNumeric b);
327 inline GncNumeric operator/(GncNumeric a, int64_t b)
328 {
329  return a / GncNumeric(b, 1);
330 }
331 inline GncNumeric operator/(int64_t a, GncNumeric b)
332 {
333  return b / GncNumeric(a, 1);
334 }
342 std::ostream& operator<<(std::ostream&, GncNumeric);
343 
344 /* Implementation adapted from "The C++ Standard Library, Second Edition" by
345  * Nicolai M. Josuttis, Addison-Wesley, 2012, ISBN 978-0-321-62321-8.
346  */
347 template <typename charT, typename traits>
348 std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& s, GncNumeric n)
349 {
350  std::basic_ostringstream<charT, traits> ss;
351  std::locale loc = s.getloc();
352  ss.imbue(loc);
353  auto dec_pt = static_cast<charT>('.');
354  try
355  {
356  dec_pt = std::use_facet<std::numpunct<charT>>(loc).decimal_point();
357  }
358  catch(const std::bad_cast& err) {} //Don't do anything, num_sep is already set.
359 
360  ss.copyfmt(s);
361  ss.width(0);
362  if (n.denom() == 1)
363  ss << n.num();
364  else if (n.is_decimal())
365  ss << n.num() / n.denom() << dec_pt
366  << (n.num() > 0 ? n.num() : -n.num()) % n.denom();
367  else
368  ss << n.num() << "/" << n.denom();
369  s << ss.str();
370  return s;
371 }
372 
383 /* Implementation adapted from "The C++ Standard Library, Second Edition" by
384  * Nicolai M. Josuttis, Addison-Wesley, 2012, ISBN 978-0-321-62321-8.
385  */
386 template <typename charT, typename traits>
387 std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& s, GncNumeric& n)
388 {
389  std::basic_string<charT, traits> instr;
390  s >> instr;
391  if (s)
392  n = GncNumeric(instr, true);
393  return s;
394 }
395 
399 inline int cmp(GncNumeric a, GncNumeric b) { return a.cmp(b); }
400 inline int cmp(GncNumeric a, int64_t b) { return a.cmp(b); }
401 inline int cmp(int64_t a, GncNumeric b) { return GncNumeric(a, 1).cmp(b); }
402 
408 inline bool operator<(GncNumeric a, GncNumeric b) { return cmp(a, b) < 0; }
409 inline bool operator<(GncNumeric a, int64_t b) { return cmp(a, b) < 0; }
410 inline bool operator<(int64_t a, GncNumeric b) { return cmp(a, b) < 0; }
411 inline bool operator>(GncNumeric a, GncNumeric b) { return cmp(a, b) > 0; }
412 inline bool operator>(GncNumeric a, int64_t b) { return cmp(a, b) > 0; }
413 inline bool operator>(int64_t a, GncNumeric b) { return cmp(a, b) > 0; }
414 inline bool operator==(GncNumeric a, GncNumeric b) { return cmp(a, b) == 0; }
415 inline bool operator==(GncNumeric a, int64_t b) { return cmp(a, b) == 0; }
416 inline bool operator==(int64_t a, GncNumeric b) { return cmp(a, b) == 0; }
417 inline bool operator<=(GncNumeric a, GncNumeric b) { return cmp(a, b) <= 0; }
418 inline bool operator<=(GncNumeric a, int64_t b) { return cmp(a, b) <= 0; }
419 inline bool operator<=(int64_t a, GncNumeric b) { return cmp(a, b) <= 0; }
420 inline bool operator>=(GncNumeric a, GncNumeric b) { return cmp(a, b) >= 0; }
421 inline bool operator>=(GncNumeric a, int64_t b) { return cmp(a, b) >= 0; }
422 inline bool operator>=(int64_t a, GncNumeric b) { return cmp(a, b) >= 0; }
423 inline bool operator!=(GncNumeric a, GncNumeric b) { return cmp(a, b) != 0; }
424 inline bool operator!=(GncNumeric a, int64_t b) { return cmp(a, b) != 0; }
425 inline bool operator!=(int64_t a, GncNumeric b) { return cmp(a, b) != 0; }
432 int64_t powten(unsigned int digits);
433 
434 #endif // __GNC_NUMERIC_HPP__
GncNumeric operator-() const noexcept
GncNumeric(int64_t num, int64_t denom)
Integer constructor.
Definition: gnc-numeric.hpp:76
The primary numeric class for representing amounts and values.
Definition: gnc-numeric.hpp:59
std::string to_string() const noexcept
Return a string representation of the GncNumeric.
GncNumeric abs() const noexcept
GncNumeric reduce() const noexcept
Return an equivalent fraction with all common factors between the numerator and the denominator remov...
GncNumeric to_decimal(unsigned int max_places=17) const
Convert the numeric to have a power-of-10 denominator if possible without rounding.
Rational number class using GncInt128 for the numerator and denominator.
GncNumeric(gnc_numeric in)
gnc_numeric constructor, used for interfacing old code.
Definition: gnc-numeric.hpp:96
GncNumeric()
Default constructor provides the zero value.
Definition: gnc-numeric.hpp:65
GncNumeric convert(int64_t new_denom) const
Convert a GncNumeric to use a new denominator.
GncNumeric convert_sigfigs(unsigned int figs) const
Convert with the specified sigfigs.
int64_t num() const noexcept
Accessor for numerator value.
GncNumeric inv() const noexcept
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
bool is_decimal() const noexcept
int64_t denom() const noexcept
Accessor for denominator value.