1 /********************************************************************
2  * gnc-numeric.c -- an exact-number library for accounting use      *
3  * Copyright (C) 2000 Bill Gribble                                  *
4  * Copyright (C) 2004 Linas Vepstas <linas@linas.org>               *
5  *                                                                  *
6  * This program is free software; you can redistribute it and/or    *
7  * modify it under the terms of the GNU General Public License as   *
8  * published by the Free Software Foundation; either version 2 of   *
9  * the License, or (at your option) any later version.              *
10  *                                                                  *
11  * This program is distributed in the hope that it will be useful,  *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
14  * GNU General Public License for more details.                     *
15  *                                                                  *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact:                        *
18  *                                                                  *
19  * Free Software Foundation           Voice:  +1-617-542-5942       *
20  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
21  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
22  *                                                                  *
23  *******************************************************************/
24 
25 #include <glib.h>
26 
27 #include <cmath>
28 #include <cstdio>
29 #include <cstdlib>
30 #include <cstring>
31 #include <cstdint>
32 #include <sstream>
33 #include <boost/regex.hpp>
34 #include <boost/locale/encoding_utf.hpp>
35 
36 extern "C"
37 {
38 #include <config.h>
39 #include "qof.h"
40 }
41 
42 #include "gnc-numeric.hpp"
43 #include "gnc-rational.hpp"
44 
45 static QofLogModule log_module = "qof";
46 
47 static const uint8_t max_leg_digits{17};
48 static const int64_t pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
49                                10000000, 100000000, 1000000000,
50                                INT64_C(10000000000), INT64_C(100000000000),
51                                INT64_C(1000000000000), INT64_C(10000000000000),
52                                INT64_C(100000000000000),
53                                INT64_C(10000000000000000),
54                                INT64_C(100000000000000000),
55                                INT64_C(1000000000000000000)};
56 #define POWTEN_OVERFLOW -5
57 
58 int64_t
powten(unsigned int exp)59 powten (unsigned int exp)
60 {
61     if (exp > max_leg_digits)
62         exp = max_leg_digits;
63     return pten[exp];
64 }
65 
GncNumeric(GncRational rr)66 GncNumeric::GncNumeric(GncRational rr)
67 {
68     /* Can't use isValid here because we want to throw different exceptions. */
69     if (rr.num().isNan() || rr.denom().isNan())
70         throw std::underflow_error("Operation resulted in NaN.");
71     if (rr.num().isOverflow() || rr.denom().isOverflow())
72         throw std::overflow_error("Operation overflowed a 128-bit int.");
73     if (rr.num().isBig() || rr.denom().isBig())
74     {
75         GncRational reduced(rr.reduce());
76         rr = reduced.round_to_numeric(); // A no-op if it's already small.
77     }
78     m_num = static_cast<int64_t>(rr.num());
79     m_den = static_cast<int64_t>(rr.denom());
80 }
81 
GncNumeric(double d)82 GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
83 {
84     static uint64_t max_leg_value{INT64_C(1000000000000000000)};
85     if (std::isnan(d) || fabs(d) > max_leg_value)
86     {
87         std::ostringstream msg;
88         msg << "Unable to construct a GncNumeric from " << d << ".\n";
89         throw std::invalid_argument(msg.str());
90     }
91     constexpr auto max_num = static_cast<double>(INT64_MAX);
92     auto logval = log10(fabs(d));
93     int64_t den;
94     uint8_t den_digits;
95     if (logval > 0.0)
96         den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval) + 1.0);
97     else
98         den_digits = max_leg_digits;
99     den = powten(den_digits);
100     auto num_d = static_cast<double>(den) * d;
101     while (fabs(num_d) > max_num && den_digits > 1)
102     {
103         den = powten(--den_digits);
104         num_d = static_cast<double>(den) * d;
105     }
106     auto num = static_cast<int64_t>(floor(num_d));
107 
108     if (num == 0)
109         return;
110     GncNumeric q(num, den);
111     auto r = q.reduce();
112     m_num = r.num();
113     m_den = r.denom();
114 }
115 
116 using boost::regex;
117 using boost::smatch;
118 using boost::regex_search;
GncNumeric(const std::string & str,bool autoround)119 GncNumeric::GncNumeric(const std::string& str, bool autoround)
120 {
121     static const std::string numer_frag("(-?[0-9]*)");
122     static const std::string denom_frag("([0-9]+)");
123     static const std::string hex_frag("(0x[a-f0-9]+)");
124     static const std::string slash( "[ \\t]*/[ \\t]*");
125     /* The llvm standard C++ library refused to recognize the - in the
126      * numer_frag patter with the default ECMAScript syntax so we use the awk
127      * syntax.
128      */
129     static const regex numeral(numer_frag);
130     static const regex hex(hex_frag);
131     static const regex numeral_rational(numer_frag + slash + denom_frag);
132     static const regex hex_rational(hex_frag + slash + hex_frag);
133     static const regex hex_over_num(hex_frag + slash + denom_frag);
134     static const regex num_over_hex(numer_frag + slash + hex_frag);
135     static const regex decimal(numer_frag + "[.,]" + denom_frag);
136     smatch m;
137 /* The order of testing the regexes is from the more restrictve to the less
138  * restrictive, as less-restrictive ones will match patterns that would also
139  * match the more-restrictive and so invoke the wrong construction.
140  */
141     if (str.empty())
142         throw std::invalid_argument("Can't construct a GncNumeric from an empty string.");
143     if (regex_search(str, m, hex_rational))
144     {
145         GncNumeric n(stoll(m[1].str(), nullptr, 16),
146                      stoll(m[2].str(), nullptr, 16));
147         m_num = n.num();
148         m_den = n.denom();
149         return;
150     }
151     if (regex_search(str, m, hex_over_num))
152     {
153         GncNumeric n(stoll(m[1].str(), nullptr, 16),
154                      stoll(m[2].str()));
155         m_num = n.num();
156         m_den = n.denom();
157         return;
158     }
159     if (regex_search(str, m, num_over_hex))
160     {
161         GncNumeric n(stoll(m[1].str()),
162                      stoll(m[2].str(), nullptr, 16));
163         m_num = n.num();
164         m_den = n.denom();
165         return;
166     }
167     if (regex_search(str, m, numeral_rational))
168     {
169         GncNumeric n(stoll(m[1].str()), stoll(m[2].str()));
170         m_num = n.num();
171         m_den = n.denom();
172         return;
173     }
174     if (regex_search(str, m, decimal))
175     {
176         auto neg = (m[1].length() && m[1].str()[0] == '-');
177         GncInt128 high((neg && m[1].length() > 1) || (!neg && m[1].length()) ?
178                        stoll(m[1].str()) : 0);
179         GncInt128 low(stoll(m[2].str()));
180         int64_t d = powten(m[2].str().length());
181         GncInt128 n = high * d + (neg ? -low : low);
182 
183         if (!autoround && n.isBig())
184         {
185             std::ostringstream errmsg;
186             errmsg << "Decimal string " << m[1].str() << "." << m[2].str()
187                    << "can't be represented in a GncNumeric without rounding.";
188             throw std::overflow_error(errmsg.str());
189         }
190         while (n.isBig() && d > 0)
191         {
192             n >>= 1;
193             d >>= 1;
194         }
195         if (n.isBig()) //Shouldn't happen, of course
196         {
197             std::ostringstream errmsg;
198             errmsg << "Decimal string " << m[1].str() << "." << m[2].str()
199             << " can't be represented in a GncNumeric, even after reducing denom to " << d;
200             throw std::overflow_error(errmsg.str());
201         }
202         GncNumeric gncn(static_cast<int64_t>(n), d);
203         m_num = gncn.num();
204         m_den = gncn.denom();
205         return;
206     }
207     if (regex_search(str, m, hex))
208     {
209         GncNumeric n(stoll(m[1].str(), nullptr, 16),INT64_C(1));
210         m_num = n.num();
211         m_den = n.denom();
212         return;
213     }
214     if (regex_search(str, m, numeral))
215     {
216         GncNumeric n(stoll(m[1].str()), INT64_C(1));
217         m_num = n.num();
218         m_den = n.denom();
219         return;
220     }
221     std::ostringstream errmsg;
222     errmsg << "String " << str << " contains no recognizable numeric value.";
223     throw std::invalid_argument(errmsg.str());
224 }
225 
operator gnc_numeric() const226 GncNumeric::operator gnc_numeric() const noexcept
227 {
228     return {m_num, m_den};
229 }
230 
operator double() const231 GncNumeric::operator double() const noexcept
232 {
233     return static_cast<double>(m_num) / static_cast<double>(m_den);
234 }
235 
236 GncNumeric
operator -() const237 GncNumeric::operator-() const noexcept
238 {
239     GncNumeric b(*this);
240     b.m_num = - b.m_num;
241     return b;
242 }
243 
244 GncNumeric
inv() const245 GncNumeric::inv() const noexcept
246 {
247     if (m_num == 0)
248         return *this;
249     if (m_num < 0)
250         return GncNumeric(-m_den, -m_num);
251     return GncNumeric(m_den, m_num);
252 }
253 
254 GncNumeric
abs() const255 GncNumeric::abs() const noexcept
256 {
257     if (m_num < 0)
258         return -*this;
259     return *this;
260 }
261 
262 GncNumeric
reduce() const263 GncNumeric::reduce() const noexcept
264 {
265     return static_cast<GncNumeric>(GncRational(*this).reduce());
266 }
267 
268 GncNumeric::round_param
prepare_conversion(int64_t new_denom) const269 GncNumeric::prepare_conversion(int64_t new_denom) const
270 {
271     if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
272         return {m_num, m_den, 0};
273     GncRational conversion(new_denom, m_den);
274     auto red_conv = conversion.reduce();
275     GncInt128 old_num(m_num);
276     auto new_num = old_num * red_conv.num();
277     auto rem = new_num % red_conv.denom();
278     new_num /= red_conv.denom();
279     if (new_num.isBig())
280     {
281         GncRational rr(new_num, new_denom);
282         GncNumeric nn(rr);
283         rr = rr.convert<RoundType::truncate>(new_denom);
284         return {static_cast<int64_t>(rr.num()), new_denom, 0};
285     }
286     return {static_cast<int64_t>(new_num),
287             static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
288 }
289 
290 int64_t
sigfigs_denom(unsigned figs) const291 GncNumeric::sigfigs_denom(unsigned figs) const noexcept
292 {
293     if (m_num == 0)
294         return 1;
295 
296     int64_t num_abs{std::abs(m_num)};
297     bool not_frac = num_abs > m_den;
298     int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
299     unsigned digits{};
300     while (val >= 10)
301     {
302         ++digits;
303         val /= 10;
304     }
305     return not_frac ?
306             powten(digits < figs ? figs - digits - 1 : 0) :
307             powten(figs + digits);
308 }
309 
310 std::string
to_string() const311 GncNumeric::to_string() const noexcept
312 {
313     std::ostringstream out;
314     out << *this;
315     return out.str();
316 }
317 
318 bool
is_decimal() const319 GncNumeric::is_decimal() const noexcept
320 {
321     for (unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
322     {
323         if (m_den == pten[pwr])
324             return true;
325         if (m_den % pten[pwr])
326             return false;
327     }
328     return false;
329 }
330 
331 GncNumeric
to_decimal(unsigned int max_places) const332 GncNumeric::to_decimal(unsigned int max_places) const
333 {
334     if (max_places > max_leg_digits)
335         max_places = max_leg_digits;
336 
337     if (m_num == 0)
338 	return GncNumeric();
339 
340     if (is_decimal())
341     {
342         if (m_num == 0 || m_den < powten(max_places))
343             return *this; // Nothing to do.
344         /* See if we can reduce m_num to fit in max_places */
345         auto excess = m_den / powten(max_places);
346         if (m_num % excess)
347         {
348             std::ostringstream msg;
349             msg << "GncNumeric " << *this
350                 << " could not be represented in " << max_places
351                 << " decimal places without rounding.\n";
352             throw std::range_error(msg.str());
353         }
354         return GncNumeric(m_num / excess, powten(max_places));
355     }
356     GncRational rr(*this);
357     rr = rr.convert<RoundType::never>(powten(max_places)); //May throw
358     /* rr might have gotten reduced a bit too much; if so, put it back: */
359     unsigned int pwr{1};
360     for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
361     auto reduce_to = powten(pwr);
362     GncInt128 rr_num(rr.num()), rr_den(rr.denom());
363     if (rr_den % reduce_to)
364     {
365         auto factor(reduce_to / rr.denom());
366         rr_num *= factor;
367         rr_den *= factor;
368     }
369     while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
370     {
371         rr_num /= 10;
372         rr_den /= 10;
373     }
374     try
375     {
376         /* Construct from the parts to avoid the GncRational constructor's
377          * automatic rounding.
378          */
379         return {static_cast<int64_t>(rr_num), static_cast<int64_t>(rr_den)};
380     }
381     catch (const std::invalid_argument& err)
382     {
383         std::ostringstream msg;
384         msg << "GncNumeric " << *this
385             << " could not be represented as a decimal without rounding.\n";
386         throw std::range_error(msg.str());
387     }
388     catch (const std::overflow_error& err)
389     {
390         std::ostringstream msg;
391         msg << "GncNumeric " << *this
392             << " overflows when attempting to convert it to decimal.\n";
393         throw std::range_error(msg.str());
394     }
395     catch (const std::underflow_error& err)
396     {
397         std::ostringstream msg;
398         msg << "GncNumeric " << *this
399             << " underflows when attempting to convert it to decimal.\n";
400         throw std::range_error(msg.str());
401     }
402 }
403 
404 void
operator +=(GncNumeric b)405 GncNumeric::operator+=(GncNumeric b)
406 {
407     *this = *this + b;
408 }
409 
410 void
operator -=(GncNumeric b)411 GncNumeric::operator-=(GncNumeric b)
412 {
413     *this = *this - b;
414 }
415 
416 void
operator *=(GncNumeric b)417 GncNumeric::operator*=(GncNumeric b)
418 {
419     *this = *this * b;
420 }
421 
422 void
operator /=(GncNumeric b)423 GncNumeric::operator/=(GncNumeric b)
424 {
425     *this = *this / b;
426 }
427 
428 int
cmp(GncNumeric b)429 GncNumeric::cmp(GncNumeric b)
430 {
431     if (m_den == b.denom())
432     {
433         auto b_num = b.num();
434         return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
435     }
436     GncRational an(*this), bn(b);
437     return an.cmp(bn);
438 }
439 
440 GncNumeric
operator +(GncNumeric a,GncNumeric b)441 operator+(GncNumeric a, GncNumeric b)
442 {
443     if (a.num() == 0)
444         return b;
445     if (b.num() == 0)
446         return a;
447     GncRational ar(a), br(b);
448     auto rr = ar + br;
449     return static_cast<GncNumeric>(rr);
450 }
451 
452 GncNumeric
operator -(GncNumeric a,GncNumeric b)453 operator-(GncNumeric a, GncNumeric b)
454 {
455     return a + (-b);
456 }
457 
458 GncNumeric
operator *(GncNumeric a,GncNumeric b)459 operator*(GncNumeric a, GncNumeric b)
460 {
461     if (a.num() == 0 || b.num() == 0)
462     {
463         GncNumeric retval;
464         return retval;
465     }
466     GncRational ar(a), br(b);
467     auto rr = ar * br;
468     return static_cast<GncNumeric>(rr);
469 }
470 
471 GncNumeric
operator /(GncNumeric a,GncNumeric b)472 operator/(GncNumeric a, GncNumeric b)
473 {
474     if (a.num() == 0)
475     {
476         GncNumeric retval;
477         return retval;
478     }
479     if (b.num() == 0)
480         throw std::underflow_error("Attempt to divide by zero.");
481 
482     GncRational ar(a), br(b);
483     auto rr = ar / br;
484     return static_cast<GncNumeric>(rr);
485 }
486 
487 template <typename T, typename I> T
488 convert(T num, I new_denom, int how)
489 {
490     auto rtype = static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK);
491     unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
492 
493     auto dtype = static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK);
494     bool sigfigs = dtype == DenomType::sigfigs;
495     if (dtype == DenomType::reduce)
496         num = num.reduce();
497 
498     switch (rtype)
499     {
500         case RoundType::floor:
501             if (sigfigs)
502                 return num.template convert_sigfigs<RoundType::floor>(figs);
503             else
504                 return num.template convert<RoundType::floor>(new_denom);
505         case RoundType::ceiling:
506             if (sigfigs)
507                 return num.template convert_sigfigs<RoundType::ceiling>(figs);
508             else
509                 return num.template convert<RoundType::ceiling>(new_denom);
510         case RoundType::truncate:
511             if (sigfigs)
512                 return num.template convert_sigfigs<RoundType::truncate>(figs);
513             else
514                 return num.template convert<RoundType::truncate>(new_denom);
515         case RoundType::promote:
516             if (sigfigs)
517                 return num.template convert_sigfigs<RoundType::promote>(figs);
518             else
519                 return num.template convert<RoundType::promote>(new_denom);
520         case RoundType::half_down:
521             if (sigfigs)
522                 return num.template convert_sigfigs<RoundType::half_down>(figs);
523             else
524                 return num.template convert<RoundType::half_down>(new_denom);
525         case RoundType::half_up:
526             if (sigfigs)
527                 return num.template convert_sigfigs<RoundType::half_up>(figs);
528             else
529                 return num.template convert<RoundType::half_up>(new_denom);
530         case RoundType::bankers:
531             if (sigfigs)
532                 return num.template convert_sigfigs<RoundType::bankers>(figs);
533             else
534                 return num.template convert<RoundType::bankers>(new_denom);
535         case RoundType::never:
536             if (sigfigs)
537                 return num.template convert_sigfigs<RoundType::never>(figs);
538             else
539                 return num.template convert<RoundType::never>(new_denom);
540         default:
541             /* round-truncate just returns the numerator unchanged. The old
542              * gnc-numeric convert had no "default" behavior at rounding that
543              * had the same result, but we need to make it explicit here to
544              * run the rest of the conversion code.
545              */
546             if (sigfigs)
547                 return num.template convert_sigfigs<RoundType::truncate>(figs);
548             else
549                 return num.template convert<RoundType::truncate>(new_denom);
550 
551     }
552 }
553 
554 /* =============================================================== */
555 /* This function is small, simple, and used everywhere below,
556  * lets try to inline it.
557  */
558 GNCNumericErrorCode
gnc_numeric_check(gnc_numeric in)559 gnc_numeric_check(gnc_numeric in)
560 {
561     if (G_LIKELY(in.denom != 0))
562     {
563         return GNC_ERROR_OK;
564     }
565     else if (in.num)
566     {
567         if ((0 < in.num) || (-4 > in.num))
568         {
569             in.num = (gint64) GNC_ERROR_OVERFLOW;
570         }
571         return (GNCNumericErrorCode) in.num;
572     }
573     else
574     {
575         return GNC_ERROR_ARG;
576     }
577 }
578 
579 
580 /* *******************************************************************
581  *  gnc_numeric_zero_p
582  ********************************************************************/
583 
584 gboolean
gnc_numeric_zero_p(gnc_numeric a)585 gnc_numeric_zero_p(gnc_numeric a)
586 {
587     if (gnc_numeric_check(a))
588     {
589         return 0;
590     }
591     else
592     {
593         if ((a.num == 0) && (a.denom != 0))
594         {
595             return 1;
596         }
597         else
598         {
599             return 0;
600         }
601     }
602 }
603 
604 /* *******************************************************************
605  *  gnc_numeric_negative_p
606  ********************************************************************/
607 
608 gboolean
gnc_numeric_negative_p(gnc_numeric a)609 gnc_numeric_negative_p(gnc_numeric a)
610 {
611     if (gnc_numeric_check(a))
612     {
613         return 0;
614     }
615     else
616     {
617         if ((a.num < 0) && (a.denom != 0))
618         {
619             return 1;
620         }
621         else
622         {
623             return 0;
624         }
625     }
626 }
627 
628 /* *******************************************************************
629  *  gnc_numeric_positive_p
630  ********************************************************************/
631 
632 gboolean
gnc_numeric_positive_p(gnc_numeric a)633 gnc_numeric_positive_p(gnc_numeric a)
634 {
635     if (gnc_numeric_check(a))
636     {
637         return 0;
638     }
639     else
640     {
641         if ((a.num > 0) && (a.denom != 0))
642         {
643             return 1;
644         }
645         else
646         {
647             return 0;
648         }
649     }
650 }
651 
652 
653 /* *******************************************************************
654  *  gnc_numeric_compare
655  *  returns 1 if a>b, -1 if b>a, 0 if a == b
656  ********************************************************************/
657 
658 int
gnc_numeric_compare(gnc_numeric a,gnc_numeric b)659 gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
660 {
661     gint64 aa, bb;
662 
663     if (gnc_numeric_check(a) || gnc_numeric_check(b))
664     {
665         return 0;
666     }
667 
668     if (a.denom == b.denom)
669     {
670         if (a.num == b.num) return 0;
671         if (a.num > b.num) return 1;
672         return -1;
673     }
674 
675     GncNumeric an (a), bn (b);
676 
677     return an.cmp(bn);
678 }
679 
680 
681 /* *******************************************************************
682  *  gnc_numeric_eq
683  ********************************************************************/
684 
685 gboolean
gnc_numeric_eq(gnc_numeric a,gnc_numeric b)686 gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
687 {
688     return ((a.num == b.num) && (a.denom == b.denom));
689 }
690 
691 
692 /* *******************************************************************
693  *  gnc_numeric_equal
694  ********************************************************************/
695 
696 gboolean
gnc_numeric_equal(gnc_numeric a,gnc_numeric b)697 gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
698 {
699     if (gnc_numeric_check(a))
700     {
701         /* a is not a valid number, check b */
702         if (gnc_numeric_check(b))
703             /* Both invalid, consider them equal */
704             return TRUE;
705         else
706             /* a invalid, b valid */
707             return FALSE;
708     }
709     if (gnc_numeric_check(b))
710         /* a valid, b invalid */
711         return FALSE;
712 
713     return gnc_numeric_compare (a, b) == 0;
714 }
715 
716 
717 /* *******************************************************************
718  *  gnc_numeric_same
719  *  would a and b be equal() if they were both converted to the same
720  *  denominator?
721  ********************************************************************/
722 
723 int
gnc_numeric_same(gnc_numeric a,gnc_numeric b,gint64 denom,gint how)724 gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
725                  gint how)
726 {
727     gnc_numeric aconv, bconv;
728 
729     aconv = gnc_numeric_convert(a, denom, how);
730     bconv = gnc_numeric_convert(b, denom, how);
731 
732     return(gnc_numeric_equal(aconv, bconv));
733 }
734 
735 static int64_t
denom_lcd(gnc_numeric a,gnc_numeric b,int64_t denom,int how)736 denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
737 {
738     if (denom == GNC_DENOM_AUTO &&
739         (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
740     {
741         GncInt128 ad(a.denom), bd(b.denom);
742         denom = static_cast<int64_t>(ad.lcm(bd));
743     }
744     return denom;
745 }
746 
747 /* *******************************************************************
748  *  gnc_numeric_add
749  ********************************************************************/
750 
751 gnc_numeric
gnc_numeric_add(gnc_numeric a,gnc_numeric b,gint64 denom,gint how)752 gnc_numeric_add(gnc_numeric a, gnc_numeric b,
753                 gint64 denom, gint how)
754 {
755     if (gnc_numeric_check(a) || gnc_numeric_check(b))
756     {
757         return gnc_numeric_error(GNC_ERROR_ARG);
758     }
759     try
760     {
761         denom = denom_lcd(a, b, denom, how);
762         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
763         {
764             GncNumeric an (a), bn (b);
765             GncNumeric sum = an + bn;
766             return static_cast<gnc_numeric>(convert(sum, denom, how));
767         }
768         GncRational ar(a), br(b);
769         auto sum = ar + br;
770         if (denom == GNC_DENOM_AUTO &&
771             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
772             return static_cast<gnc_numeric>(sum.round_to_numeric());
773         sum = convert(sum, denom, how);
774         if (sum.is_big() || !sum.valid())
775             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
776         return static_cast<gnc_numeric>(sum);
777     }
778     catch (const std::overflow_error& err)
779     {
780         PWARN("%s", err.what());
781         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
782     }
783     catch (const std::invalid_argument& err)
784     {
785         PWARN("%s", err.what());
786         return gnc_numeric_error(GNC_ERROR_ARG);
787     }
788     catch (const std::underflow_error& err)
789     {
790         PWARN("%s", err.what());
791         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
792     }
793     catch (const std::domain_error& err)
794     {
795         PWARN("%s", err.what());
796         return gnc_numeric_error(GNC_ERROR_REMAINDER);
797     }
798 }
799 
800 /* *******************************************************************
801  *  gnc_numeric_sub
802  ********************************************************************/
803 
804 gnc_numeric
gnc_numeric_sub(gnc_numeric a,gnc_numeric b,gint64 denom,gint how)805 gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
806                 gint64 denom, gint how)
807 {
808     gnc_numeric nb;
809     if (gnc_numeric_check(a) || gnc_numeric_check(b))
810     {
811         return gnc_numeric_error(GNC_ERROR_ARG);
812     }
813     try
814     {
815         denom = denom_lcd(a, b, denom, how);
816         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
817         {
818             GncNumeric an (a), bn (b);
819             auto sum = an - bn;
820             return static_cast<gnc_numeric>(convert(sum, denom, how));
821         }
822         GncRational ar(a), br(b);
823         auto sum = ar - br;
824         if (denom == GNC_DENOM_AUTO &&
825             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
826             return static_cast<gnc_numeric>(sum.round_to_numeric());
827         sum = convert(sum, denom, how);
828         if (sum.is_big() || !sum.valid())
829             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
830         return static_cast<gnc_numeric>(sum);
831     }
832     catch (const std::overflow_error& err)
833     {
834         PWARN("%s", err.what());
835         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
836     }
837     catch (const std::invalid_argument& err)
838     {
839         PWARN("%s", err.what());
840         return gnc_numeric_error(GNC_ERROR_ARG);
841     }
842     catch (const std::underflow_error& err)
843     {
844         PWARN("%s", err.what());
845         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
846     }
847     catch (const std::domain_error& err)
848     {
849         PWARN("%s", err.what());
850         return gnc_numeric_error(GNC_ERROR_REMAINDER);
851     }
852 }
853 
854 /* *******************************************************************
855  *  gnc_numeric_mul
856  ********************************************************************/
857 
858 gnc_numeric
gnc_numeric_mul(gnc_numeric a,gnc_numeric b,gint64 denom,gint how)859 gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
860                 gint64 denom, gint how)
861 {
862     if (gnc_numeric_check(a) || gnc_numeric_check(b))
863     {
864         return gnc_numeric_error(GNC_ERROR_ARG);
865     }
866 
867     try
868     {
869         denom = denom_lcd(a, b, denom, how);
870         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
871         {
872             GncNumeric an (a), bn (b);
873             auto prod = an * bn;
874             return static_cast<gnc_numeric>(convert(prod, denom, how));
875         }
876         GncRational ar(a), br(b);
877         auto prod = ar * br;
878         if (denom == GNC_DENOM_AUTO &&
879             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
880             return static_cast<gnc_numeric>(prod.round_to_numeric());
881         prod = convert(prod, denom, how);
882         if (prod.is_big() || !prod.valid())
883             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
884         return static_cast<gnc_numeric>(prod);
885      }
886     catch (const std::overflow_error& err)
887     {
888         PWARN("%s", err.what());
889         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
890     }
891     catch (const std::invalid_argument& err)
892     {
893         PWARN("%s", err.what());
894         return gnc_numeric_error(GNC_ERROR_ARG);
895     }
896     catch (const std::underflow_error& err)
897     {
898         PWARN("%s", err.what());
899         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
900     }
901     catch (const std::domain_error& err)
902     {
903         PWARN("%s", err.what());
904         return gnc_numeric_error(GNC_ERROR_REMAINDER);
905     }
906 }
907 
908 
909 /* *******************************************************************
910  *  gnc_numeric_div
911  ********************************************************************/
912 
913 gnc_numeric
gnc_numeric_div(gnc_numeric a,gnc_numeric b,gint64 denom,gint how)914 gnc_numeric_div(gnc_numeric a, gnc_numeric b,
915                 gint64 denom, gint how)
916 {
917     if (gnc_numeric_check(a) || gnc_numeric_check(b))
918     {
919         return gnc_numeric_error(GNC_ERROR_ARG);
920     }
921     try
922     {
923         denom = denom_lcd(a, b, denom, how);
924         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
925         {
926             GncNumeric an (a), bn (b);
927             auto quot = an / bn;
928             return static_cast<gnc_numeric>(convert(quot, denom, how));
929         }
930         GncRational ar(a), br(b);
931         auto quot = ar / br;
932         if (denom == GNC_DENOM_AUTO &&
933             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
934             return static_cast<gnc_numeric>(quot.round_to_numeric());
935         quot =  static_cast<gnc_numeric>(convert(quot, denom, how));
936         if (quot.is_big() || !quot.valid())
937             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
938         return static_cast<gnc_numeric>(quot);
939     }
940     catch (const std::overflow_error& err)
941     {
942         PWARN("%s", err.what());
943         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
944     }
945     catch (const std::invalid_argument& err)
946     {
947         PWARN("%s", err.what());
948         return gnc_numeric_error(GNC_ERROR_ARG);
949     }
950     catch (const std::underflow_error& err) //Divide by zero
951     {
952         PWARN("%s", err.what());
953         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
954     }
955     catch (const std::domain_error& err)
956     {
957         PWARN("%s", err.what());
958         return gnc_numeric_error(GNC_ERROR_REMAINDER);
959     }
960 }
961 
962 /* *******************************************************************
963  *  gnc_numeric_neg
964  *  negate the argument
965  ********************************************************************/
966 
967 gnc_numeric
gnc_numeric_neg(gnc_numeric a)968 gnc_numeric_neg(gnc_numeric a)
969 {
970     if (gnc_numeric_check(a))
971     {
972         return gnc_numeric_error(GNC_ERROR_ARG);
973     }
974     return gnc_numeric_create(- a.num, a.denom);
975 }
976 
977 /* *******************************************************************
978  *  gnc_numeric_abs
979  *  return the absolute value of the argument
980  ********************************************************************/
981 
982 gnc_numeric
gnc_numeric_abs(gnc_numeric a)983 gnc_numeric_abs(gnc_numeric a)
984 {
985     if (gnc_numeric_check(a))
986     {
987         return gnc_numeric_error(GNC_ERROR_ARG);
988     }
989     return gnc_numeric_create(ABS(a.num), a.denom);
990 }
991 
992 
993 /* *******************************************************************
994  *  gnc_numeric_convert
995  ********************************************************************/
996 
997 gnc_numeric
gnc_numeric_convert(gnc_numeric in,int64_t denom,int how)998 gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
999 {
1000     if (gnc_numeric_check(in))
1001         return in;
1002     try
1003     {
1004         return convert(GncNumeric(in), denom, how);
1005     }
1006     catch (const std::invalid_argument& err)
1007     {
1008         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1009     }
1010     catch (const std::overflow_error& err)
1011     {
1012         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1013     }
1014     catch (const std::underflow_error& err)
1015     {
1016         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1017     }
1018     catch (const std::domain_error& err)
1019     {
1020         return gnc_numeric_error(GNC_ERROR_REMAINDER);
1021     }
1022 }
1023 
1024 
1025 /* *******************************************************************
1026  *  reduce a fraction by GCF elimination.  This is NOT done as a
1027  *  part of the arithmetic API unless GNC_HOW_DENOM_REDUCE is specified
1028  *  as the output denominator.
1029  ********************************************************************/
1030 
1031 gnc_numeric
gnc_numeric_reduce(gnc_numeric in)1032 gnc_numeric_reduce(gnc_numeric in)
1033 {
1034     if (gnc_numeric_check(in))
1035     {
1036         return gnc_numeric_error(GNC_ERROR_ARG);
1037     }
1038 
1039     if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
1040         return in;
1041     try
1042     {
1043         GncNumeric an (in);
1044         return static_cast<gnc_numeric>(an.reduce());
1045     }
1046     catch (const std::overflow_error& err)
1047     {
1048         PWARN("%s", err.what());
1049         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1050     }
1051     catch (const std::invalid_argument& err)
1052     {
1053         PWARN("%s", err.what());
1054         return gnc_numeric_error(GNC_ERROR_ARG);
1055     }
1056     catch (const std::underflow_error& err)
1057     {
1058         PWARN("%s", err.what());
1059         return gnc_numeric_error(GNC_ERROR_ARG);
1060     }
1061     catch (const std::domain_error& err)
1062     {
1063         PWARN("%s", err.what());
1064         return gnc_numeric_error(GNC_ERROR_REMAINDER);
1065     }
1066 }
1067 
1068 
1069 /* *******************************************************************
1070  * gnc_numeric_to_decimal
1071  *
1072  * Attempt to convert the denominator to an exact power of ten without
1073  * rounding. TRUE is returned if 'a' has been converted or was already
1074  * decimal. Otherwise, FALSE is returned and 'a' remains unchanged.
1075  * The 'max_decimal_places' parameter may be NULL.
1076  ********************************************************************/
1077 
1078 gboolean
gnc_numeric_to_decimal(gnc_numeric * a,guint8 * max_decimal_places)1079 gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
1080 {
1081     int max_places =  max_decimal_places == NULL ? max_leg_digits :
1082         *max_decimal_places;
1083     if (a->num == 0) return TRUE;
1084     try
1085     {
1086         GncNumeric an (*a);
1087         auto bn = an.to_decimal(max_places);
1088         *a = static_cast<gnc_numeric>(bn);
1089         return TRUE;
1090     }
1091     catch (const std::exception& err)
1092     {
1093         PWARN("%s", err.what());
1094         return FALSE;
1095     }
1096 }
1097 
1098 
1099 gnc_numeric
gnc_numeric_invert(gnc_numeric num)1100 gnc_numeric_invert(gnc_numeric num)
1101 {
1102     if (num.num == 0)
1103         return gnc_numeric_zero();
1104     try
1105     {
1106         return static_cast<gnc_numeric>(GncNumeric(num).inv());
1107     }
1108     catch (const std::overflow_error& err)
1109     {
1110         PWARN("%s", err.what());
1111         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1112     }
1113     catch (const std::invalid_argument& err)
1114     {
1115         PWARN("%s", err.what());
1116         return gnc_numeric_error(GNC_ERROR_ARG);
1117     }
1118     catch (const std::underflow_error& err)
1119     {
1120         PWARN("%s", err.what());
1121         return gnc_numeric_error(GNC_ERROR_ARG);
1122     }
1123     catch (const std::domain_error& err)
1124     {
1125         PWARN("%s", err.what());
1126         return gnc_numeric_error(GNC_ERROR_REMAINDER);
1127     }
1128 }
1129 
1130 /* *******************************************************************
1131  *  double_to_gnc_numeric
1132  ********************************************************************/
1133 
1134 #ifdef _MSC_VER
1135 # define rint /* */
1136 #endif
1137 gnc_numeric
double_to_gnc_numeric(double in,gint64 denom,gint how)1138 double_to_gnc_numeric(double in, gint64 denom, gint how)
1139 {
1140     try
1141     {
1142         GncNumeric an(in);
1143         return convert(an, denom, how);
1144     }
1145     catch (const std::overflow_error& err)
1146     {
1147         PWARN("%s", err.what());
1148         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1149     }
1150     catch (const std::invalid_argument& err)
1151     {
1152         PWARN("%s", err.what());
1153         return gnc_numeric_error(GNC_ERROR_ARG);
1154     }
1155     catch (const std::underflow_error& err)
1156     {
1157         PWARN("%s", err.what());
1158         return gnc_numeric_error(GNC_ERROR_ARG);
1159     }
1160     catch (const std::domain_error& err)
1161     {
1162         PWARN("%s", err.what());
1163         return gnc_numeric_error(GNC_ERROR_REMAINDER);
1164     }
1165 }
1166 
1167 /* *******************************************************************
1168  *  gnc_numeric_to_double
1169  ********************************************************************/
1170 
1171 double
gnc_numeric_to_double(gnc_numeric in)1172 gnc_numeric_to_double(gnc_numeric in)
1173 {
1174     if (in.denom > 0)
1175     {
1176         return (double)in.num / (double)in.denom;
1177     }
1178     else
1179     {
1180         return (double)(in.num * -in.denom);
1181     }
1182 }
1183 
1184 /* *******************************************************************
1185  *  gnc_numeric_error
1186  ********************************************************************/
1187 
1188 gnc_numeric
gnc_numeric_error(GNCNumericErrorCode error_code)1189 gnc_numeric_error(GNCNumericErrorCode error_code)
1190 {
1191     return gnc_numeric_create(error_code, 0LL);
1192 }
1193 
1194 
1195 
1196 /* *******************************************************************
1197  *  gnc_numeric text IO
1198  ********************************************************************/
1199 
1200 gchar *
gnc_numeric_to_string(gnc_numeric n)1201 gnc_numeric_to_string(gnc_numeric n)
1202 {
1203     gchar *result;
1204     gint64 tmpnum = n.num;
1205     gint64 tmpdenom = n.denom;
1206 
1207     result = g_strdup_printf("%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, tmpdenom);
1208 
1209     return result;
1210 }
1211 
1212 gchar *
gnc_num_dbg_to_string(gnc_numeric n)1213 gnc_num_dbg_to_string(gnc_numeric n)
1214 {
1215     static char buff[1000];
1216     static char *p = buff;
1217     gint64 tmpnum = n.num;
1218     gint64 tmpdenom = n.denom;
1219 
1220     p += 100;
1221     if (p - buff >= 1000) p = buff;
1222 
1223     sprintf(p, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, tmpdenom);
1224 
1225     return p;
1226 }
1227 
1228 gboolean
string_to_gnc_numeric(const gchar * str,gnc_numeric * n)1229 string_to_gnc_numeric(const gchar* str, gnc_numeric *n)
1230 {
1231     try
1232     {
1233         GncNumeric an(str);
1234         *n = static_cast<gnc_numeric>(an);
1235         return TRUE;
1236     }
1237     catch (const std::exception& err)
1238     {
1239         PWARN("%s", err.what());
1240         return FALSE;
1241     }
1242 }
1243 
1244 /* *******************************************************************
1245  *  GValue handling
1246  ********************************************************************/
1247 static gpointer
gnc_numeric_boxed_copy_func(gpointer in_ptr)1248 gnc_numeric_boxed_copy_func( gpointer in_ptr )
1249 {
1250     auto in_gnc_numeric = static_cast<gnc_numeric*>(in_ptr);
1251     if (!in_gnc_numeric)
1252         return nullptr;
1253 
1254     /* newvalue will be passed to g_free so we must allocate with g_malloc. */
1255     auto newvalue = static_cast<gnc_numeric*>(g_malloc (sizeof (gnc_numeric)));
1256     *newvalue = *in_gnc_numeric;
1257 
1258     return newvalue;
1259 }
1260 
1261 static void
gnc_numeric_boxed_free_func(gpointer in_gnc_numeric)1262 gnc_numeric_boxed_free_func( gpointer in_gnc_numeric )
1263 {
1264     g_free( in_gnc_numeric );
1265 }
1266 
1267 GType
gnc_numeric_get_type(void)1268 gnc_numeric_get_type( void )
1269 {
1270     static GType type = 0;
1271 
1272     if ( type == 0 )
1273     {
1274         type = g_boxed_type_register_static( "gnc_numeric",
1275                                              gnc_numeric_boxed_copy_func,
1276                                              gnc_numeric_boxed_free_func );
1277     }
1278 
1279     return type;
1280 }
1281 
1282 /* *******************************************************************
1283  *  gnc_numeric misc testing
1284  ********************************************************************/
1285 #ifdef _GNC_NUMERIC_TEST
1286 
1287 static char *
gnc_numeric_print(gnc_numeric in)1288 gnc_numeric_print(gnc_numeric in)
1289 {
1290     char * retval;
1291     if (gnc_numeric_check(in))
1292     {
1293         retval = g_strdup_printf("<ERROR> [%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
1294                                  in.num,
1295                                  in.denom);
1296     }
1297     else
1298     {
1299         retval = g_strdup_printf("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
1300                                  in.num,
1301                                  in.denom);
1302     }
1303     return retval;
1304 }
1305 
1306 int
main(int argc,char ** argv)1307 main(int argc, char ** argv)
1308 {
1309     gnc_numeric a = gnc_numeric_create(1, 3);
1310     gnc_numeric b = gnc_numeric_create(1, 4);
1311     gnc_numeric c;
1312 
1313     gnc_numeric err;
1314 
1315 
1316     printf("multiply (EXACT): %s * %s = %s\n",
1317            gnc_numeric_print(a), gnc_numeric_print(b),
1318            gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT)));
1319 
1320     printf("multiply (REDUCE): %s * %s = %s\n",
1321            gnc_numeric_print(a), gnc_numeric_print(b),
1322            gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE)));
1323 
1324 
1325     return 0;
1326 }
1327 #endif
1328 
1329 
1330 std::ostream&
operator <<(std::ostream & s,GncNumeric n)1331 operator<<(std::ostream& s, GncNumeric n)
1332 {
1333     using boost::locale::conv::utf_to_utf;
1334     std::basic_ostringstream<wchar_t> ss;
1335     ss.imbue(s.getloc());
1336     ss << n;
1337     s << utf_to_utf<char>(ss.str());
1338     return s;
1339 }
1340 
gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)1341 const char* gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
1342 {
1343     switch (error_code)
1344     {
1345     case GNC_ERROR_OK:
1346         return "GNC_ERROR_OK";
1347     case GNC_ERROR_ARG:
1348         return "GNC_ERROR_ARG";
1349     case GNC_ERROR_OVERFLOW:
1350         return "GNC_ERROR_OVERFLOW";
1351     case GNC_ERROR_DENOM_DIFF:
1352         return "GNC_ERROR_DENOM_DIFF";
1353     case GNC_ERROR_REMAINDER:
1354         return "GNC_ERROR_REMAINDER";
1355     default:
1356         return "<unknown>";
1357     }
1358 }
1359 
1360 /* ======================== END OF FILE =================== */
1361