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 <cstdint>
31 #include "gnc-rational-rounding.hpp"
32 
33 class GncRational;
34 
61 {
62 public:
66  GncNumeric() : m_num (0), m_den(1) {}
77  GncNumeric(int64_t num, int64_t denom) :
78  m_num(num), m_den(denom) {
79  if (denom == 0)
80  throw std::invalid_argument("Attempt to construct a GncNumeric with a 0 denominator.");
81  }
97  GncNumeric(gnc_numeric in) : m_num(in.num), m_den(in.denom)
98  {
99  if (in.denom == 0)
100  throw std::invalid_argument("Attempt to construct a GncNumeric with a 0 denominator.");
101  /* gnc_numeric has a dumb convention that a negative denominator means
102  * to multiply the numerator by the denominator instead of dividing.
103  */
104  if (in.denom < 0)
105  {
106  m_num *= -in.denom;
107  m_den = 1;
108  }
109  }
118  GncNumeric(double d);
119 
140  GncNumeric(const std::string& str, bool autoround=false);
141  GncNumeric(const GncNumeric& rhs) = default;
142  GncNumeric(GncNumeric&& rhs) = default;
143  GncNumeric& operator=(const GncNumeric& rhs) = default;
144  GncNumeric& operator=(GncNumeric&& rhs) = default;
145  ~GncNumeric() = default;
149  operator gnc_numeric() const noexcept;
153  operator double() const noexcept;
154 
158  int64_t num() const noexcept { return m_num; }
162  int64_t denom() const noexcept { return m_den; }
166  GncNumeric operator-() const noexcept;
170  GncNumeric inv() const noexcept;
174  GncNumeric abs() const noexcept;
181  GncNumeric reduce() const noexcept;
192  template <RoundType RT>
193  GncNumeric convert(int64_t new_denom) const
194  {
195  auto params = prepare_conversion(new_denom);
196  if (new_denom == GNC_DENOM_AUTO)
197  new_denom = m_den;
198  if (params.rem == 0)
199  return GncNumeric(params.num, new_denom);
200  return GncNumeric(round(params.num, params.den,
201  params.rem, RT2T<RT>()), new_denom);
202  }
203 
215  template <RoundType RT>
216  GncNumeric convert_sigfigs(unsigned int figs) const
217  {
218  auto new_denom(sigfigs_denom(figs));
219  auto params = prepare_conversion(new_denom);
220  if (new_denom == 0) //It had better not, but just in case...
221  new_denom = 1;
222  if (params.rem == 0)
223  return GncNumeric(params.num, new_denom);
224  return GncNumeric(round(params.num, params.den,
225  params.rem, RT2T<RT>()), new_denom);
226  }
231  std::string to_string() const noexcept;
235  bool is_decimal() const noexcept;
244  GncNumeric to_decimal(unsigned int max_places=17) const;
254  void operator+=(GncNumeric b);
255  void operator-=(GncNumeric b);
256  void operator*=(GncNumeric b);
257  void operator/=(GncNumeric b);
258  /* @} */
265  int cmp(GncNumeric b);
266  int cmp(int64_t b) { return cmp(GncNumeric(b, 1)); }
268 private:
269  struct round_param
270  {
271  int64_t num;
272  int64_t den;
273  int64_t rem;
274  };
275  /* Calculates the denominator required to convert to figs sigfigs. */
276  int64_t sigfigs_denom(unsigned figs) const noexcept;
277  /* Calculates a round_param struct to pass to a rounding function that will
278  * finish computing a GncNumeric with the new denominator.
279  */
280  round_param prepare_conversion(int64_t new_denom) const;
281  int64_t m_num;
282  int64_t m_den;
283 };
284 
300 GncNumeric operator+(GncNumeric a, GncNumeric b);
301 inline GncNumeric operator+(GncNumeric a, int64_t b)
302 {
303  return a + GncNumeric(b, 1);
304 }
305 inline GncNumeric operator+(int64_t a, GncNumeric b)
306 {
307  return b + GncNumeric(a, 1);
308 }
309 GncNumeric operator-(GncNumeric a, GncNumeric b);
310 inline GncNumeric operator-(GncNumeric a, int64_t b)
311 {
312  return a - GncNumeric(b, 1);
313 }
314 inline GncNumeric operator-(int64_t a, GncNumeric b)
315 {
316  return b - GncNumeric(a, 1);
317 }
318 GncNumeric operator*(GncNumeric a, GncNumeric b);
319 inline GncNumeric operator*(GncNumeric a, int64_t b)
320 {
321  return a * GncNumeric(b, 1);
322 }
323 inline GncNumeric operator*(int64_t a, GncNumeric b)
324 {
325  return b * GncNumeric(a, 1);
326 }
327 GncNumeric operator/(GncNumeric a, GncNumeric b);
328 inline GncNumeric operator/(GncNumeric a, int64_t b)
329 {
330  return a / GncNumeric(b, 1);
331 }
332 inline GncNumeric operator/(int64_t a, GncNumeric b)
333 {
334  return b / GncNumeric(a, 1);
335 }
343 std::ostream& operator<<(std::ostream&, GncNumeric);
344 
345 /* Implementation adapted from "The C++ Standard Library, Second Edition" by
346  * Nicolai M. Josuttis, Addison-Wesley, 2012, ISBN 978-0-321-62321-8.
347  */
348 template <typename charT, typename traits>
349 std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& s, GncNumeric n)
350 {
351  std::basic_ostringstream<charT, traits> ss;
352  std::locale loc = s.getloc();
353  ss.imbue(loc);
354  auto dec_pt = static_cast<charT>('.');
355  try
356  {
357  dec_pt = std::use_facet<std::numpunct<charT>>(loc).decimal_point();
358  }
359  catch(const std::bad_cast& err) {} //Don't do anything, num_sep is already set.
360 
361  ss.copyfmt(s);
362  ss.width(0);
363  if (n.denom() == 1)
364  ss << n.num();
365  else if (n.is_decimal())
366  ss << n.num() / n.denom() << dec_pt
367  << (n.num() > 0 ? n.num() : -n.num()) % n.denom();
368  else
369  ss << n.num() << "/" << n.denom();
370  s << ss.str();
371  return s;
372 }
373 
384 /* Implementation adapted from "The C++ Standard Library, Second Edition" by
385  * Nicolai M. Josuttis, Addison-Wesley, 2012, ISBN 978-0-321-62321-8.
386  */
387 template <typename charT, typename traits>
388 std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& s, GncNumeric& n)
389 {
390  std::basic_string<charT, traits> instr;
391  s >> instr;
392  if (s)
393  n = GncNumeric(instr, true);
394  return s;
395 }
396 
400 inline int cmp(GncNumeric a, GncNumeric b) { return a.cmp(b); }
401 inline int cmp(GncNumeric a, int64_t b) { return a.cmp(b); }
402 inline int cmp(int64_t a, GncNumeric b) { return GncNumeric(a, 1).cmp(b); }
403 
409 inline bool operator<(GncNumeric a, GncNumeric b) { return cmp(a, b) < 0; }
410 inline bool operator<(GncNumeric a, int64_t b) { return cmp(a, b) < 0; }
411 inline bool operator<(int64_t a, GncNumeric b) { return cmp(a, b) < 0; }
412 inline bool operator>(GncNumeric a, GncNumeric b) { return cmp(a, b) > 0; }
413 inline bool operator>(GncNumeric a, int64_t b) { return cmp(a, b) > 0; }
414 inline bool operator>(int64_t a, GncNumeric b) { return cmp(a, b) > 0; }
415 inline bool operator==(GncNumeric a, GncNumeric b) { return cmp(a, b) == 0; }
416 inline bool operator==(GncNumeric a, int64_t b) { return cmp(a, b) == 0; }
417 inline bool operator==(int64_t a, GncNumeric b) { return cmp(a, b) == 0; }
418 inline bool operator<=(GncNumeric a, GncNumeric b) { return cmp(a, b) <= 0; }
419 inline bool operator<=(GncNumeric a, int64_t b) { return cmp(a, b) <= 0; }
420 inline bool operator<=(int64_t a, GncNumeric b) { return cmp(a, b) <= 0; }
421 inline bool operator>=(GncNumeric a, GncNumeric b) { return cmp(a, b) >= 0; }
422 inline bool operator>=(GncNumeric a, int64_t b) { return cmp(a, b) >= 0; }
423 inline bool operator>=(int64_t a, GncNumeric b) { return cmp(a, b) >= 0; }
424 inline bool operator!=(GncNumeric a, GncNumeric b) { return cmp(a, b) != 0; }
425 inline bool operator!=(GncNumeric a, int64_t b) { return cmp(a, b) != 0; }
426 inline bool operator!=(int64_t a, GncNumeric b) { return cmp(a, b) != 0; }
433 int64_t powten(unsigned int digits);
434 
435 #endif // __GNC_NUMERIC_HPP__
GncNumeric operator-() const noexcept
GncNumeric(int64_t num, int64_t denom)
Integer constructor.
Definition: gnc-numeric.hpp:77
The primary numeric class for representing amounts and values.
Definition: gnc-numeric.hpp:60
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:97
GncNumeric()
Default constructor provides the zero value.
Definition: gnc-numeric.hpp:66
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.