LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-numeric.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 59.9 % 763 457
Test Date: 2026-11-02 18:19:43 Functions: 88.5 % 61 54
Legend: Lines:     hit not hit

            Line data    Source code
       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/regex/icu.hpp>
      35              : #include <boost/locale/encoding_utf.hpp>
      36              : 
      37              : #include <config.h>
      38              : #include <stdexcept>
      39              : #include <stdint.h>
      40              : #include "gnc-int128.hpp"
      41              : #include "qof.h"
      42              : 
      43              : #include "gnc-numeric.hpp"
      44              : #include "gnc-rational.hpp"
      45              : 
      46              : #include <optional>
      47              : #include <charconv>
      48              : 
      49              : static QofLogModule log_module = "qof";
      50              : 
      51              : static const uint8_t max_leg_digits{18};
      52              : static const int64_t pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
      53              :                                10000000, 100000000, 1000000000,
      54              :                                INT64_C(10000000000), INT64_C(100000000000),
      55              :                                INT64_C(1000000000000), INT64_C(10000000000000),
      56              :                                INT64_C(100000000000000),
      57              :                                INT64_C(1000000000000000),
      58              :                                INT64_C(10000000000000000),
      59              :                                INT64_C(100000000000000000),
      60              :                                INT64_C(1000000000000000000)};
      61              : #define POWTEN_OVERFLOW -5
      62              : 
      63              : int64_t
      64        25684 : powten (unsigned int exp)
      65              : {
      66        25684 :     if (exp > max_leg_digits)
      67          602 :         exp = max_leg_digits;
      68        25684 :     return pten[exp];
      69              : }
      70              : 
      71       195441 : GncNumeric::GncNumeric(GncRational rr)
      72              : {
      73              :     /* Can't use isValid here because we want to throw different exceptions. */
      74       195441 :     if (rr.num().isNan() || rr.denom().isNan())
      75            0 :         throw std::underflow_error("Operation resulted in NaN.");
      76       195441 :     if (rr.num().isOverflow() || rr.denom().isOverflow())
      77            0 :         throw std::overflow_error("Operation overflowed a 128-bit int.");
      78       195441 :     if (rr.num().isBig() || rr.denom().isBig())
      79              :     {
      80           66 :         GncRational reduced(rr.reduce());
      81           66 :         rr = reduced.round_to_numeric(); // A no-op if it's already small.
      82              :     }
      83       195413 :     m_num = static_cast<int64_t>(rr.num());
      84       195413 :     m_den = static_cast<int64_t>(rr.denom());
      85       195413 : }
      86              : 
      87          179 : GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
      88              : {
      89              :     static uint64_t max_leg_value{INT64_C(1000000000000000000)};
      90          179 :     if (std::isnan(d) || fabs(d) > max_leg_value)
      91              :     {
      92            0 :         std::ostringstream msg;
      93            0 :         msg << "Unable to construct a GncNumeric from " << d << ".\n";
      94            0 :         throw std::invalid_argument(msg.str());
      95            0 :     }
      96          179 :     constexpr auto max_num = static_cast<double>(INT64_MAX);
      97          179 :     auto logval = log10(fabs(d));
      98              :     int64_t den;
      99              :     uint8_t den_digits;
     100          179 :     if (logval > 0.0)
     101          165 :         den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval));
     102              :     else
     103           14 :         den_digits = max_leg_digits;
     104          179 :     den = powten(den_digits);
     105          179 :     auto num_d = static_cast<double>(den) * d;
     106          320 :     while (fabs(num_d) > max_num && den_digits > 1)
     107              :     {
     108          141 :         den = powten(--den_digits);
     109          141 :         num_d = static_cast<double>(den) * d;
     110              :     }
     111          179 :     auto num = static_cast<int64_t>(floor(num_d));
     112              : 
     113          179 :     if (num == 0)
     114            6 :         return;
     115          173 :     GncNumeric q(num, den);
     116          173 :     auto r = q.reduce();
     117          173 :     m_num = r.num();
     118          173 :     m_den = r.denom();
     119              : }
     120              : 
     121              : using boost::regex;
     122              : using boost::u32regex;
     123              : using boost::regex_search;
     124              : using boost::u32regex_search;
     125              : using boost::smatch;
     126              : 
     127              : 
     128              : static std::pair<int64_t, int64_t>
     129           14 : reduce_number_pair(std::pair<GncInt128, GncInt128>num_pair,
     130              :                    const std::string& num_str, bool autoround)
     131              : {
     132           14 :     auto [n, d] = num_pair;
     133           14 :     if (!autoround && n.isBig()) {
     134            0 :         std::ostringstream errmsg;
     135              :         errmsg << "Decimal string " << num_str
     136            0 :                << "can't be represented in a GncNumeric without rounding.";
     137            0 :         throw std::overflow_error(errmsg.str());
     138            0 :     }
     139           14 :     while (n.isBig() && d > 0) {
     140            0 :         n >>= 1;
     141            0 :         d >>= 1;
     142              :     }
     143           14 :     if (n.isBig()) // Shouldn't happen, of course
     144              :     {
     145            0 :         std::ostringstream errmsg;
     146              :         errmsg << "Decimal string " << num_str
     147              :                << " can't be represented in a GncNumeric, even after reducing "
     148            0 :             "denom to "
     149            0 :                << d;
     150            0 :         throw std::overflow_error(errmsg.str());
     151            0 :     }
     152           14 :     return std::make_pair(static_cast<int64_t>(n), static_cast<int64_t>(d));
     153              : }
     154              : 
     155              : static std::pair<GncInt128, int64_t>
     156           14 : numeric_from_decimal_match(const std::string& integer, const std::string& decimal)
     157              : {
     158           14 :     auto neg = (!integer.empty() && integer[0] == '-');
     159           14 :     GncInt128 high((neg && integer.length() > 1) || (!neg && !integer.empty())
     160           13 :                    ? stoll(integer)
     161           28 :                    : 0);
     162           14 :     GncInt128 low{ decimal.empty() ? 0 : stoll(decimal)};
     163           14 :     auto exp10 = decimal.length();
     164           14 :     int64_t d = powten(exp10);
     165           14 :     GncInt128 n = high * d + (neg ? -low : low);
     166           14 :     while (exp10 > max_leg_digits) {
     167              :         /* If the arg to powten is bigger than max_leg_digits
     168              :            it returns 10**max_leg_digits so reduce exp10 by
     169              :            that amount */
     170            0 :         n = n / powten(exp10 - max_leg_digits);
     171            0 :         exp10 -= max_leg_digits;
     172              :     }
     173           28 :     return std::make_pair(n, d);
     174              : }
     175              : 
     176              : static std::pair<GncInt128, GncInt128>
     177            0 : numeric_from_scientific_match(smatch &m)
     178              : {
     179            0 :     int exp{m[4].matched ? stoi(m[4].str()) : 0};
     180            0 :     auto neg_exp{exp < 0};
     181            0 :     exp = neg_exp ? -exp : exp;
     182            0 :     if (exp >= max_leg_digits)
     183              :     {
     184            0 :         std::ostringstream errmsg;
     185            0 :         errmsg << "Exponent " << m[3].str() << " in match " << m[0].str()
     186            0 :                << " exceeds range that GnuCash can parse.";
     187            0 :         throw std::overflow_error(errmsg.str());
     188            0 :     }
     189              : 
     190            0 :     GncInt128 num, denom;
     191            0 :     auto mult = powten(exp);
     192              : 
     193            0 :     if (m[1].matched)
     194              :     {
     195            0 :         denom = neg_exp ? mult : 1;
     196            0 :         num = neg_exp ? stoll(m[1].str()) : mult * stoll(m[1].str());
     197              :     }
     198              :     else
     199              :     {
     200            0 :         auto [d_num, d_denom] = numeric_from_decimal_match(m[2].str(), m[3].str());
     201              : 
     202            0 :         if (neg_exp || d_denom > mult)
     203              :         {
     204            0 :             num = d_num;
     205            0 :             denom = neg_exp ? d_denom * mult : d_denom / mult;
     206              :         }
     207              :         else
     208              :         {
     209            0 :             num = d_num * mult / d_denom;
     210            0 :             denom = 1;
     211              :         }
     212              :     }
     213            0 :     return std::make_pair(num, denom);
     214              : }
     215              : 
     216              : static std::optional<gnc_numeric>
     217         5195 : fast_numeral_rational (const char* str)
     218              : {
     219         5195 :     if (!str || !str[0])
     220            1 :         return {};
     221              : 
     222              :     // because minint64 = -9223372036854775808 and has 20 characters,
     223              :     // the maximum strlen to handle is 20+19+1 = 40. 48 is a nice
     224              :     // number in 64-bit.
     225         5194 :     auto end_ptr{(const char*)memchr (str, '\0', 48)};
     226         5194 :     if (!end_ptr)
     227            0 :         return {};
     228              : 
     229         5194 :     int64_t num, denom{};
     230         5194 :     auto result = std::from_chars (str, end_ptr, num);
     231         5194 :     if (result.ec != std::errc())
     232            6 :         return {};
     233              : 
     234         5188 :     if (result.ptr == end_ptr)
     235            1 :         return gnc_numeric_create (num, 1);
     236              : 
     237         5187 :     if (*result.ptr != '/')
     238           20 :         return {};
     239              : 
     240         5167 :     result = std::from_chars (result.ptr + 1, end_ptr, denom);
     241         5167 :     if (result.ec != std::errc() || result.ptr != end_ptr || denom <= 0)
     242            0 :         return {};
     243              : 
     244         5167 :     return gnc_numeric_create (num, denom);
     245              : }
     246              : 
     247           19 : GncNumeric::GncNumeric(const std::string &str, bool autoround) {
     248           27 :     static const std::string begin("^[^-.0-9]*");
     249           27 :     static const std::string end("[^0-9]*$");
     250           27 :     static const std::string begin_group("(?:");
     251           27 :     static const std::string end_group(")");
     252           27 :     static const std::string or_op("|");
     253           27 :     static const std::string maybe_sign ("(-?)");
     254           27 :     static const std::string opt_signed_int("(-?[0-9]*)");
     255           27 :     static const std::string opt_signed_separated_int("(-?[0-9]{1,3})");
     256           27 :     static const std::string unsigned_int("([0-9]+)");
     257           27 :     static const std::string eu_separated_int("(?:[[:space:]'.]([0-9]{3}))?");
     258           27 :     static const std::string en_separated_int("(?:\\,([0-9]{3}))?");
     259           27 :     static const std::string eu_decimal_part("(?:\\,([0-9]+))?");
     260           27 :     static const std::string en_decimal_part("(?:\\.([0-9]+))?");
     261           27 :     static const std::string hex_frag("(0[xX][A-Fa-f0-9]+)");
     262           27 :     static const std::string slash("[ \\t]*/[ \\t]*");
     263           27 :     static const std::string whitespace("[ \\t]+");
     264           19 :     static const std::string eu_sep_decimal(begin_group + opt_signed_separated_int + eu_separated_int + eu_separated_int + eu_separated_int + eu_separated_int + eu_decimal_part + end_group);
     265           19 :     static const std::string en_sep_decimal(begin_group + opt_signed_separated_int + en_separated_int + en_separated_int + en_separated_int + en_separated_int + en_decimal_part + end_group);
     266              :     /* The llvm standard C++ library refused to recognize the - in the
     267              :      * opt_signed_int pattern with the default ECMAScript syntax so we use the
     268              :      * awk syntax.
     269              :      */
     270           19 :     static const regex numeral(begin + opt_signed_int + end);
     271           19 :     static const regex hex(begin + hex_frag + end);
     272           19 :     static const regex numeral_rational(begin + opt_signed_int + slash + unsigned_int + end);
     273           19 :     static const regex integer_and_fraction(begin + maybe_sign + unsigned_int + whitespace + unsigned_int + slash + unsigned_int + end);
     274           19 :     static const regex hex_rational(begin + hex_frag + slash + hex_frag + end);
     275           19 :     static const regex hex_over_num(begin + hex_frag + slash + unsigned_int + end);
     276           19 :     static const regex num_over_hex(begin + opt_signed_int + slash + hex_frag + end);
     277           19 :     static const regex decimal(begin + opt_signed_int + "[.,]" + unsigned_int + end);
     278              :     static const u32regex sep_decimal =
     279           19 :         boost::make_u32regex(begin + begin_group + eu_sep_decimal + or_op + en_sep_decimal + end_group + end);
     280           19 :     static const regex scientific("(?:(-?[0-9]+[.,]?)|(-?[0-9]*)[.,]([0-9]+))[Ee](-?[0-9]+)");
     281           19 :     static const regex has_hex_prefix(".*0[xX]$");
     282           57 :     smatch m, x;
     283              :     /* The order of testing the regexes is from the more restrictve to the less
     284              :      * restrictive, as less-restrictive ones will match  patterns that would also
     285              :      * match the more-restrictive and so invoke the wrong construction.
     286              :      */
     287           19 :     if (str.empty())
     288              :         throw std::invalid_argument(
     289            1 :             "Can't construct a GncNumeric from an empty string.");
     290           18 :     if (auto res = fast_numeral_rational (str.c_str()))
     291              :     {
     292            0 :         m_num = res->num;
     293            0 :         m_den = res->denom;
     294            0 :         return;
     295              :     }
     296           18 :     if (regex_search(str, m, hex_rational))
     297              :     {
     298            0 :         GncNumeric n(stoll(m[1].str(), nullptr, 16),
     299            0 :                      stoll(m[2].str(), nullptr, 16));
     300              : 
     301            0 :         m_num = n.num();
     302            0 :         m_den = n.denom();
     303            0 :         return;
     304              :     }
     305           18 :     if (regex_search(str, m, hex_over_num))
     306              :     {
     307            0 :         GncNumeric n(stoll(m[1].str(), nullptr, 16), stoll(m[2].str()));
     308            0 :         m_num = n.num();
     309            0 :         m_den = n.denom();
     310            0 :         return;
     311              :     }
     312           18 :     if (regex_search(str, m, num_over_hex))
     313              :     {
     314            0 :         GncNumeric n(stoll(m[1].str()), stoll(m[2].str(), nullptr, 16));
     315            0 :         m_num = n.num();
     316            0 :         m_den = n.denom();
     317            0 :         return;
     318              :     }
     319           18 :     if (regex_search(str, m, integer_and_fraction))
     320              :     {
     321            2 :         GncNumeric n(stoll(m[3].str()), stoll(m[4].str()));
     322            2 :         n += stoll(m[2].str());
     323            2 :         m_num = m[1].str().empty() ? n.num() : -n.num();
     324            2 :         m_den = n.denom();
     325            2 :         return;
     326              :     }
     327           16 :     if (regex_search(str, m, numeral_rational))
     328              :     {
     329            0 :         GncNumeric n(stoll(m[1].str()), stoll(m[2].str()));
     330            0 :         m_num = n.num();
     331            0 :         m_den = n.denom();
     332            0 :         return;
     333              :     }
     334           16 :     if (regex_search(str, m, scientific) && ! regex_match(m.prefix().str(), x,  has_hex_prefix))
     335              :     {
     336            0 :         auto [num, denom] =
     337            0 :             reduce_number_pair(numeric_from_scientific_match(m),
     338            0 :                                    str, autoround);
     339            0 :         m_num = num;
     340            0 :         m_den = denom;
     341            0 :         return;
     342              :     }
     343           16 :     if (regex_search(str, m, decimal))
     344              :     {
     345           14 :         std::string integer{m[1].matched ? m[1].str() : ""};
     346           14 :         std::string decimal{m[2].matched ? m[2].str() : ""};
     347           14 :         auto [num, denom] = reduce_number_pair(numeric_from_decimal_match(integer, decimal), str, autoround);
     348           14 :         m_num = num;
     349           14 :         m_den = denom;
     350           14 :         return;
     351           14 :     }
     352            2 :     if (u32regex_search(str, m, sep_decimal))
     353              :     {
     354              :         /* There's a bit of magic here because of the complexity of
     355              :          * the regex. It supports two formats, one for locales that
     356              :          * use space, apostrophe, or dot for thousands separator and
     357              :          * comma for decimal separator and the other for locales that
     358              :          * use comma for thousands and dot for decimal. For each
     359              :          * format there are 5 captures for thousands-groups (allowing
     360              :          * up to 10^16 - 1) and one for decimal, hence the loops from
     361              :          * 1 - 5 and 7 - 11 with the decimal being either capture 6 or
     362              :          * capture 12.
     363              :          */
     364            0 :         std::string integer(""), decimal("");
     365            0 :         for (auto i{1}; i < 6; ++i)
     366            0 :             if (m[i].matched)
     367            0 :                 integer += m[i].str();
     368            0 :         if (m[6].matched)
     369            0 :             decimal += m[6].str();
     370            0 :         if (integer.empty() && decimal.empty())
     371              :         {
     372            0 :             for (auto i{7}; i <12; ++i)
     373            0 :                 if (m[i].matched)
     374            0 :                 integer += m[i].str();
     375            0 :         if (m[12].matched)
     376            0 :             decimal += m[12].str();
     377              :         }
     378            0 :         auto [num, denom] =
     379            0 :             reduce_number_pair(numeric_from_decimal_match(integer, decimal),
     380            0 :                                str, autoround);
     381            0 :         m_num = num;
     382            0 :         m_den = denom;
     383            0 :         return;
     384            0 :     }
     385            2 :     if (regex_search(str, m, hex))
     386              :     {
     387            0 :         GncNumeric n(stoll(m[1].str(), nullptr, 16), INT64_C(1));
     388            0 :         m_num = n.num();
     389            0 :         m_den = n.denom();
     390            0 :         return;
     391              :     }
     392            2 :     if (regex_search(str, m, numeral))
     393              :     {
     394            2 :         GncNumeric n(stoll(m[1].str()), INT64_C(1));
     395            0 :         m_num = n.num();
     396            0 :         m_den = n.denom();
     397            0 :         return;
     398              :     }
     399            1 :     std::ostringstream errmsg;
     400            1 :     errmsg << "String " << str << " contains no recognizable numeric value.";
     401            1 :     throw std::invalid_argument(errmsg.str());
     402           23 : }
     403              : 
     404       306029 : GncNumeric::operator gnc_numeric() const noexcept
     405              : {
     406       306029 :     return {m_num, m_den};
     407              : }
     408              : 
     409            0 : GncNumeric::operator double() const noexcept
     410              : {
     411            0 :     return static_cast<double>(m_num) / static_cast<double>(m_den);
     412              : }
     413              : 
     414              : GncNumeric
     415        14343 : GncNumeric::operator-() const noexcept
     416              : {
     417        14343 :     GncNumeric b(*this);
     418        14343 :     b.m_num = - b.m_num;
     419        14343 :     return b;
     420              : }
     421              : 
     422              : GncNumeric
     423          706 : GncNumeric::inv() const noexcept
     424              : {
     425          706 :     if (m_num == 0)
     426            0 :         return *this;
     427          706 :     if (m_num < 0)
     428            0 :         return GncNumeric(-m_den, -m_num);
     429          706 :     return GncNumeric(m_den, m_num);
     430              : }
     431              : 
     432              : GncNumeric
     433            0 : GncNumeric::abs() const noexcept
     434              : {
     435            0 :     if (m_num < 0)
     436            0 :         return -*this;
     437            0 :     return *this;
     438              : }
     439              : 
     440              : GncNumeric
     441         2670 : GncNumeric::reduce() const noexcept
     442              : {
     443         2670 :     return static_cast<GncNumeric>(GncRational(*this).reduce());
     444              : }
     445              : 
     446              : GncNumeric::round_param
     447       289422 : GncNumeric::prepare_conversion(int64_t new_denom) const
     448              : {
     449       289422 :     if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
     450       260091 :         return {m_num, m_den, 0};
     451        29331 :     GncRational conversion(new_denom, m_den);
     452        29331 :     auto red_conv = conversion.reduce();
     453        29331 :     GncInt128 old_num(m_num);
     454        29331 :     auto new_num = old_num * red_conv.num();
     455        29331 :     auto rem = new_num % red_conv.denom();
     456        29331 :     new_num /= red_conv.denom();
     457        29331 :     if (new_num.isBig())
     458              :     {
     459           15 :         GncRational rr(new_num, new_denom);
     460           15 :         GncNumeric nn(rr);
     461           15 :         rr = rr.convert<RoundType::truncate>(new_denom);
     462           15 :         return {static_cast<int64_t>(rr.num()), new_denom, 0};
     463              :     }
     464        29316 :     return {static_cast<int64_t>(new_num),
     465        29316 :             static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
     466              : }
     467              : 
     468              : int64_t
     469           31 : GncNumeric::sigfigs_denom(unsigned figs) const noexcept
     470              : {
     471           31 :     if (m_num == 0)
     472            4 :         return 1;
     473              : 
     474           27 :     int64_t num_abs{std::abs(m_num)};
     475           27 :     bool not_frac = num_abs > m_den;
     476           27 :     int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
     477           27 :     unsigned digits{};
     478           42 :     while (val >= 10)
     479              :     {
     480           15 :         ++digits;
     481           15 :         val /= 10;
     482              :     }
     483           27 :     return not_frac ? 
     484           23 :             powten(digits < figs ? figs - digits - 1 : 0) : 
     485           27 :             powten(figs + digits);
     486              : }
     487              : 
     488              : std::string
     489            0 : GncNumeric::to_string() const noexcept
     490              : {
     491            0 :     std::ostringstream out;
     492            0 :     out << *this;
     493            0 :     return out.str();
     494            0 : }
     495              : 
     496              : bool
     497        14621 : GncNumeric::is_decimal() const noexcept
     498              : {
     499        45456 :     for (unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
     500              :     {
     501        45069 :         if (m_den == pten[pwr])
     502        13959 :             return true;
     503        31110 :         if (m_den % pten[pwr])
     504          275 :             return false;
     505              :     }
     506          387 :     return false;
     507              : }
     508              : 
     509              : GncNumeric
     510        14341 : GncNumeric::to_decimal(unsigned int max_places) const
     511              : {
     512        14341 :     if (max_places > max_leg_digits)
     513            0 :         max_places = max_leg_digits;
     514              : 
     515        14341 :     if (m_num == 0)
     516            0 :         return GncNumeric();
     517              : 
     518        14341 :     if (is_decimal())
     519              :     {
     520        13679 :         if (m_num == 0 || m_den < powten(max_places))
     521        13679 :             return *this; // Nothing to do.
     522              :         /* See if we can reduce m_num to fit in max_places */
     523            0 :         auto excess = m_den / powten(max_places);
     524            0 :         if (m_num % excess)
     525              :         {
     526            0 :             std::ostringstream msg;
     527            0 :             msg << "GncNumeric " << *this
     528            0 :                 << " could not be represented in " << max_places
     529            0 :                 << " decimal places without rounding.\n";
     530            0 :             throw std::range_error(msg.str());
     531            0 :         }
     532            0 :         return GncNumeric(m_num / excess, powten(max_places));
     533              :     }
     534          662 :     GncRational rr(*this);
     535          662 :     rr = rr.convert<RoundType::never>(powten(max_places)); //May throw
     536              :     /* rr might have gotten reduced a bit too much; if so, put it back: */
     537          578 :     unsigned int pwr{1};
     538        10982 :     for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
     539          578 :     auto reduce_to = powten(pwr);
     540          578 :     GncInt128 rr_num(rr.num()), rr_den(rr.denom());
     541          578 :     if (rr_den % reduce_to)
     542              :     {
     543            0 :         auto factor(reduce_to / rr.denom());
     544            0 :         rr_num *= factor;
     545            0 :         rr_den *= factor;
     546              :     }
     547         9951 :     while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
     548              :     {
     549         9373 :         rr_num /= 10;
     550         9373 :         rr_den /= 10;
     551              :     }
     552              :     try
     553              :     {
     554              :         /* Construct from the parts to avoid the GncRational constructor's
     555              :          * automatic rounding.
     556              :          */
     557          578 :         return {static_cast<int64_t>(rr_num), static_cast<int64_t>(rr_den)};
     558              :     }
     559            0 :     catch (const std::invalid_argument& err)
     560              :     {
     561            0 :         std::ostringstream msg;
     562            0 :         msg << "GncNumeric " << *this
     563            0 :             << " could not be represented as a decimal without rounding.\n";
     564            0 :         throw std::range_error(msg.str());
     565            0 :     }
     566            0 :     catch (const std::overflow_error& err)
     567              :     {
     568            0 :         std::ostringstream msg;
     569            0 :         msg << "GncNumeric " << *this
     570            0 :             << " overflows when attempting to convert it to decimal.\n";
     571            0 :         throw std::range_error(msg.str());
     572            0 :     }
     573            0 :     catch (const std::underflow_error& err)
     574              :     {
     575            0 :         std::ostringstream msg;
     576            0 :         msg << "GncNumeric " << *this
     577            0 :             << " underflows when attempting to convert it to decimal.\n";
     578            0 :         throw std::range_error(msg.str());
     579            0 :     }
     580              : }
     581              : 
     582              : void
     583          188 : GncNumeric::operator+=(GncNumeric b)
     584              : {
     585          188 :     *this = *this + b;
     586          188 : }
     587              : 
     588              : void
     589            0 : GncNumeric::operator-=(GncNumeric b)
     590              : {
     591            0 :     *this = *this - b;
     592            0 : }
     593              : 
     594              : void
     595            0 : GncNumeric::operator*=(GncNumeric b)
     596              : {
     597            0 :     *this = *this * b;
     598            0 : }
     599              : 
     600              : void
     601            0 : GncNumeric::operator/=(GncNumeric b)
     602              : {
     603            0 :     *this = *this / b;
     604            0 : }
     605              : 
     606              : int
     607         2622 : GncNumeric::cmp(GncNumeric b)
     608              : {
     609         2622 :     if (m_den == b.denom())
     610              :     {
     611           10 :         auto b_num = b.num();
     612           10 :         return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
     613              :     }
     614         2612 :     GncRational an(*this), bn(b);
     615         2612 :     return an.cmp(bn);
     616              : }
     617              : 
     618              : GncNumeric
     619       224890 : operator+(GncNumeric a, GncNumeric b)
     620              : {
     621       224890 :     if (a.num() == 0)
     622        27099 :         return b;
     623       197791 :     if (b.num() == 0)
     624         7800 :         return a;
     625       189991 :     GncRational ar(a), br(b);
     626       189991 :     auto rr = ar + br;
     627       189991 :     return static_cast<GncNumeric>(rr);
     628              : }
     629              : 
     630              : GncNumeric
     631        14343 : operator-(GncNumeric a, GncNumeric b)
     632              : {
     633        14343 :     return a + (-b);
     634              : }
     635              : 
     636              : GncNumeric
     637         2593 : operator*(GncNumeric a, GncNumeric b)
     638              : {
     639         2593 :     if (a.num() == 0 || b.num() == 0)
     640              :     {
     641         1107 :         GncNumeric retval;
     642         1107 :         return retval;
     643              :     }
     644         1486 :     GncRational ar(a), br(b);
     645         1486 :     auto rr = ar * br;
     646         1486 :     return static_cast<GncNumeric>(rr);
     647              : }
     648              : 
     649              : GncNumeric
     650         1292 : operator/(GncNumeric a, GncNumeric b)
     651              : {
     652         1292 :     if (a.num() == 0)
     653              :     {
     654           10 :         GncNumeric retval;
     655           10 :         return retval;
     656              :     }
     657         1282 :     if (b.num() == 0)
     658            3 :         throw std::underflow_error("Attempt to divide by zero.");
     659              : 
     660         1279 :     GncRational ar(a), br(b);
     661         1279 :     auto rr = ar / br;
     662         1279 :     return static_cast<GncNumeric>(rr);
     663              : }
     664              : 
     665              : template <typename T, typename I> T
     666       292143 : convert(T num, I new_denom, int how)
     667              : {
     668       292143 :     auto rtype = static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK);
     669       292143 :     unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
     670              : 
     671       292143 :     auto dtype = static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK);
     672       292143 :     bool sigfigs = dtype == DenomType::sigfigs;
     673       292143 :     if (dtype == DenomType::reduce)
     674          979 :         num = num.reduce();
     675              : 
     676       292143 :     switch (rtype)
     677              :     {
     678           12 :         case RoundType::floor:
     679           12 :             if (sigfigs)
     680            0 :                 return num.template convert_sigfigs<RoundType::floor>(figs);
     681              :             else
     682           12 :                 return num.template convert<RoundType::floor>(new_denom);
     683            1 :         case RoundType::ceiling:
     684            1 :             if (sigfigs)
     685            0 :                 return num.template convert_sigfigs<RoundType::ceiling>(figs);
     686              :             else
     687            1 :                 return num.template convert<RoundType::ceiling>(new_denom);
     688        12453 :         case RoundType::truncate:
     689        12453 :             if (sigfigs)
     690            0 :                 return num.template convert_sigfigs<RoundType::truncate>(figs);
     691              :             else
     692        12453 :                 return num.template convert<RoundType::truncate>(new_denom);
     693            0 :         case RoundType::promote:
     694            0 :             if (sigfigs)
     695            0 :                 return num.template convert_sigfigs<RoundType::promote>(figs);
     696              :             else
     697            0 :                 return num.template convert<RoundType::promote>(new_denom);
     698            0 :         case RoundType::half_down:
     699            0 :             if (sigfigs)
     700            0 :                 return num.template convert_sigfigs<RoundType::half_down>(figs);
     701              :             else
     702            0 :                 return num.template convert<RoundType::half_down>(new_denom);
     703        43014 :         case RoundType::half_up:
     704        43014 :             if (sigfigs)
     705           25 :                 return num.template convert_sigfigs<RoundType::half_up>(figs);
     706              :             else
     707        42989 :                 return num.template convert<RoundType::half_up>(new_denom);
     708        12613 :         case RoundType::bankers:
     709        12613 :             if (sigfigs)
     710            6 :                 return num.template convert_sigfigs<RoundType::bankers>(figs);
     711              :             else
     712        12607 :                 return num.template convert<RoundType::bankers>(new_denom);
     713       221698 :         case RoundType::never:
     714       221698 :             if (sigfigs)
     715            0 :                 return num.template convert_sigfigs<RoundType::never>(figs);
     716              :             else
     717       221698 :                 return num.template convert<RoundType::never>(new_denom);
     718         2352 :         default:
     719              :             /* round-truncate just returns the numerator unchanged. The old
     720              :              * gnc-numeric convert had no "default" behavior at rounding that
     721              :              * had the same result, but we need to make it explicit here to
     722              :              * run the rest of the conversion code.
     723              :              */
     724         2352 :             if (sigfigs)
     725            0 :                 return num.template convert_sigfigs<RoundType::truncate>(figs);
     726              :             else
     727         2352 :                 return num.template convert<RoundType::truncate>(new_denom);
     728              : 
     729              :     }
     730              : }
     731              : 
     732              : /* =============================================================== */
     733              : /* This function is small, simple, and used everywhere below,
     734              :  * lets try to inline it.
     735              :  */
     736              : GNCNumericErrorCode
     737       908998 : gnc_numeric_check(gnc_numeric in)
     738              : {
     739       908998 :     if (G_LIKELY(in.denom != 0))
     740              :     {
     741       908592 :         return GNC_ERROR_OK;
     742              :     }
     743          406 :     else if (in.num)
     744              :     {
     745          315 :         if ((0 < in.num) || (-4 > in.num))
     746              :         {
     747            6 :             in.num = (gint64) GNC_ERROR_OVERFLOW;
     748              :         }
     749          315 :         return (GNCNumericErrorCode) in.num;
     750              :     }
     751              :     else
     752              :     {
     753           91 :         return GNC_ERROR_ARG;
     754              :     }
     755              : }
     756              : 
     757              : 
     758              : /* *******************************************************************
     759              :  *  gnc_numeric_zero_p
     760              :  ********************************************************************/
     761              : 
     762              : gboolean
     763        69946 : gnc_numeric_zero_p(gnc_numeric a)
     764              : {
     765        69946 :     if (gnc_numeric_check(a))
     766              :     {
     767            4 :         return 0;
     768              :     }
     769              :     else
     770              :     {
     771        69942 :         if ((a.num == 0) && (a.denom != 0))
     772              :         {
     773        32752 :             return 1;
     774              :         }
     775              :         else
     776              :         {
     777        37190 :             return 0;
     778              :         }
     779              :     }
     780              : }
     781              : 
     782              : /* *******************************************************************
     783              :  *  gnc_numeric_negative_p
     784              :  ********************************************************************/
     785              : 
     786              : gboolean
     787        46869 : gnc_numeric_negative_p(gnc_numeric a)
     788              : {
     789        46869 :     if (gnc_numeric_check(a))
     790              :     {
     791            4 :         return 0;
     792              :     }
     793              :     else
     794              :     {
     795        46865 :         if ((a.num < 0) && (a.denom != 0))
     796              :         {
     797         8284 :             return 1;
     798              :         }
     799              :         else
     800              :         {
     801        38581 :             return 0;
     802              :         }
     803              :     }
     804              : }
     805              : 
     806              : /* *******************************************************************
     807              :  *  gnc_numeric_positive_p
     808              :  ********************************************************************/
     809              : 
     810              : gboolean
     811         2260 : gnc_numeric_positive_p(gnc_numeric a)
     812              : {
     813         2260 :     if (gnc_numeric_check(a))
     814              :     {
     815            0 :         return 0;
     816              :     }
     817              :     else
     818              :     {
     819         2260 :         if ((a.num > 0) && (a.denom != 0))
     820              :         {
     821         1371 :             return 1;
     822              :         }
     823              :         else
     824              :         {
     825          889 :             return 0;
     826              :         }
     827              :     }
     828              : }
     829              : 
     830              : 
     831              : /* *******************************************************************
     832              :  *  gnc_numeric_compare
     833              :  *  returns 1 if a>b, -1 if b>a, 0 if a == b
     834              :  ********************************************************************/
     835              : 
     836              : int
     837        22063 : gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
     838              : {
     839        22063 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     840              :     {
     841            0 :         return 0;
     842              :     }
     843              : 
     844        22063 :     if (a.denom == b.denom)
     845              :     {
     846        19451 :         if (a.num == b.num) return 0;
     847         8195 :         if (a.num > b.num) return 1;
     848         3418 :         return -1;
     849              :     }
     850              : 
     851         2612 :     GncNumeric an (a), bn (b);
     852              : 
     853         2612 :     return an.cmp(bn);
     854              : }
     855              : 
     856              : 
     857              : /* *******************************************************************
     858              :  *  gnc_numeric_eq
     859              :  ********************************************************************/
     860              : 
     861              : gboolean
     862         3485 : gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
     863              : {
     864         3485 :     return ((a.num == b.num) && (a.denom == b.denom));
     865              : }
     866              : 
     867              : 
     868              : /* *******************************************************************
     869              :  *  gnc_numeric_equal
     870              :  ********************************************************************/
     871              : 
     872              : gboolean
     873        11952 : gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
     874              : {
     875        11952 :     if (gnc_numeric_check(a))
     876              :     {
     877              :         /* a is not a valid number, check b */
     878            0 :         if (gnc_numeric_check(b))
     879              :             /* Both invalid, consider them equal */
     880            0 :             return TRUE;
     881              :         else
     882              :             /* a invalid, b valid */
     883            0 :             return FALSE;
     884              :     }
     885        11952 :     if (gnc_numeric_check(b))
     886              :         /* a valid, b invalid */
     887            0 :         return FALSE;
     888              : 
     889        11952 :     return gnc_numeric_compare (a, b) == 0;
     890              : }
     891              : 
     892              : 
     893              : /* *******************************************************************
     894              :  *  gnc_numeric_same
     895              :  *  would a and b be equal() if they were both converted to the same
     896              :  *  denominator?
     897              :  ********************************************************************/
     898              : 
     899              : int
     900         5124 : gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
     901              :                  gint how)
     902              : {
     903              :     gnc_numeric aconv, bconv;
     904              : 
     905         5124 :     aconv = gnc_numeric_convert(a, denom, how);
     906         5124 :     bconv = gnc_numeric_convert(b, denom, how);
     907              : 
     908        10248 :     return(gnc_numeric_equal(aconv, bconv));
     909              : }
     910              : 
     911              : static int64_t
     912       238049 : denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
     913              : {
     914       238049 :     if (denom == GNC_DENOM_AUTO &&
     915       232141 :         (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
     916              :     {
     917          823 :         GncInt128 ad(a.denom), bd(b.denom);
     918          823 :         denom = static_cast<int64_t>(ad.lcm(bd));
     919              :     }
     920       238049 :     return denom;
     921              : }
     922              : 
     923              : /* *******************************************************************
     924              :  *  gnc_numeric_add
     925              :  ********************************************************************/
     926              : 
     927              : gnc_numeric
     928       217688 : gnc_numeric_add(gnc_numeric a, gnc_numeric b,
     929              :                 gint64 denom, gint how)
     930              : {
     931       217688 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     932              :     {
     933           80 :         return gnc_numeric_error(GNC_ERROR_ARG);
     934              :     }
     935              :     try
     936              :     {
     937       217608 :         denom = denom_lcd(a, b, denom, how);
     938       217608 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
     939              :         {
     940       210359 :             GncNumeric an (a), bn (b);
     941       210359 :             GncNumeric sum = an + bn;
     942       210359 :             return static_cast<gnc_numeric>(convert(sum, denom, how));
     943              :         }
     944         7249 :         GncRational ar(a), br(b);
     945         7249 :         auto sum = ar + br;
     946         7249 :         if (denom == GNC_DENOM_AUTO &&
     947         6670 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
     948         6670 :             return static_cast<gnc_numeric>(sum.round_to_numeric());
     949          579 :         sum = convert(sum, denom, how);
     950          579 :         if (sum.is_big() || !sum.valid())
     951            0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     952          579 :         return static_cast<gnc_numeric>(sum);
     953              :     }
     954            0 :     catch (const std::overflow_error& err)
     955              :     {
     956            0 :         PWARN("%s", err.what());
     957            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     958            0 :     }
     959            0 :     catch (const std::invalid_argument& err)
     960              :     {
     961            0 :         PWARN("%s", err.what());
     962            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
     963            0 :     }
     964            0 :     catch (const std::underflow_error& err)
     965              :     {
     966            0 :         PWARN("%s", err.what());
     967            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
     968            0 :     }
     969            0 :     catch (const std::domain_error& err)
     970              :     {
     971            0 :         PWARN("%s", err.what());
     972            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
     973            0 :     }
     974              : }
     975              : 
     976              : /* *******************************************************************
     977              :  *  gnc_numeric_sub
     978              :  ********************************************************************/
     979              : 
     980              : gnc_numeric
     981        14357 : gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
     982              :                 gint64 denom, gint how)
     983              : {
     984        14357 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
     985              :     {
     986            7 :         return gnc_numeric_error(GNC_ERROR_ARG);
     987              :     }
     988              :     try
     989              :     {
     990        14350 :         denom = denom_lcd(a, b, denom, how);
     991        14350 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
     992              :         {
     993        14343 :             GncNumeric an (a), bn (b);
     994        14343 :             auto sum = an - bn;
     995        14343 :             return static_cast<gnc_numeric>(convert(sum, denom, how));
     996              :         }
     997            7 :         GncRational ar(a), br(b);
     998            7 :         auto sum = ar - br;
     999            7 :         if (denom == GNC_DENOM_AUTO &&
    1000            7 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
    1001            7 :             return static_cast<gnc_numeric>(sum.round_to_numeric());
    1002            0 :         sum = convert(sum, denom, how);
    1003            0 :         if (sum.is_big() || !sum.valid())
    1004            0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1005            0 :         return static_cast<gnc_numeric>(sum);
    1006              :     }
    1007            0 :     catch (const std::overflow_error& err)
    1008              :     {
    1009            0 :         PWARN("%s", err.what());
    1010            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1011            0 :     }
    1012            0 :     catch (const std::invalid_argument& err)
    1013              :     {
    1014            0 :         PWARN("%s", err.what());
    1015            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1016            0 :     }
    1017            0 :     catch (const std::underflow_error& err)
    1018              :     {
    1019            0 :         PWARN("%s", err.what());
    1020            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1021            0 :     }
    1022            0 :     catch (const std::domain_error& err)
    1023              :     {
    1024            0 :         PWARN("%s", err.what());
    1025            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1026            0 :     }
    1027              : }
    1028              : 
    1029              : /* *******************************************************************
    1030              :  *  gnc_numeric_mul
    1031              :  ********************************************************************/
    1032              : 
    1033              : gnc_numeric
    1034         4270 : gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
    1035              :                 gint64 denom, gint how)
    1036              : {
    1037         4270 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
    1038              :     {
    1039            2 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1040              :     }
    1041              : 
    1042              :     try
    1043              :     {
    1044         4268 :         denom = denom_lcd(a, b, denom, how);
    1045         4268 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
    1046              :         {
    1047         2583 :             GncNumeric an (a), bn (b);
    1048         2583 :             auto prod = an * bn;
    1049         2555 :             return static_cast<gnc_numeric>(convert(prod, denom, how));
    1050              :         }
    1051         1685 :         GncRational ar(a), br(b);
    1052         1685 :         auto prod = ar * br;
    1053         1685 :         if (denom == GNC_DENOM_AUTO &&
    1054           16 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
    1055           16 :             return static_cast<gnc_numeric>(prod.round_to_numeric());
    1056         1669 :         prod = convert(prod, denom, how);
    1057         1669 :         if (prod.is_big() || !prod.valid())
    1058            0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1059         1669 :         return static_cast<gnc_numeric>(prod);
    1060              :      }
    1061           43 :     catch (const std::overflow_error& err)
    1062              :     {
    1063           43 :         PWARN("%s", err.what());
    1064           43 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1065           43 :     }
    1066            0 :     catch (const std::invalid_argument& err)
    1067              :     {
    1068            0 :         PWARN("%s", err.what());
    1069            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1070            0 :     }
    1071            0 :     catch (const std::underflow_error& err)
    1072              :     {
    1073            0 :         PWARN("%s", err.what());
    1074            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1075            0 :     }
    1076            0 :     catch (const std::domain_error& err)
    1077              :     {
    1078            0 :         PWARN("%s", err.what());
    1079            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1080            0 :     }
    1081              : }
    1082              : 
    1083              : 
    1084              : /* *******************************************************************
    1085              :  *  gnc_numeric_div
    1086              :  ********************************************************************/
    1087              : 
    1088              : gnc_numeric
    1089         1827 : gnc_numeric_div(gnc_numeric a, gnc_numeric b,
    1090              :                 gint64 denom, gint how)
    1091              : {
    1092         1827 :     if (gnc_numeric_check(a) || gnc_numeric_check(b))
    1093              :     {
    1094            4 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1095              :     }
    1096              :     try
    1097              :     {
    1098         1823 :         denom = denom_lcd(a, b, denom, how);
    1099         1823 :         if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
    1100              :         {
    1101         1281 :             GncNumeric an (a), bn (b);
    1102         1281 :             auto quot = an / bn;
    1103         1278 :             return static_cast<gnc_numeric>(convert(quot, denom, how));
    1104              :         }
    1105          542 :         GncRational ar(a), br(b);
    1106          542 :         auto quot = ar / br;
    1107          541 :         if (denom == GNC_DENOM_AUTO &&
    1108          541 :             (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
    1109           47 :             return static_cast<gnc_numeric>(quot.round_to_numeric());
    1110          494 :         quot =  static_cast<gnc_numeric>(convert(quot, denom, how));
    1111          494 :         if (quot.is_big() || !quot.valid())
    1112            0 :             return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1113          494 :         return static_cast<gnc_numeric>(quot);
    1114              :     }
    1115            4 :     catch (const std::overflow_error& err)
    1116              :     {
    1117            0 :         PWARN("%s", err.what());
    1118            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1119            0 :     }
    1120            0 :     catch (const std::invalid_argument& err)
    1121              :     {
    1122            0 :         PWARN("%s", err.what());
    1123            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1124            0 :     }
    1125            4 :     catch (const std::underflow_error& err) //Divide by zero
    1126              :     {
    1127            4 :         PWARN("%s", err.what());
    1128            4 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1129            4 :     }
    1130            0 :     catch (const std::domain_error& err)
    1131              :     {
    1132            0 :         PWARN("%s", err.what());
    1133            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1134            0 :     }
    1135              : }
    1136              : 
    1137              : /* *******************************************************************
    1138              :  *  gnc_numeric_neg
    1139              :  *  negate the argument
    1140              :  ********************************************************************/
    1141              : 
    1142              : gnc_numeric
    1143         2106 : gnc_numeric_neg(gnc_numeric a)
    1144              : {
    1145         2106 :     if (gnc_numeric_check(a))
    1146              :     {
    1147            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1148              :     }
    1149         2106 :     return gnc_numeric_create(- a.num, a.denom);
    1150              : }
    1151              : 
    1152              : /* *******************************************************************
    1153              :  *  gnc_numeric_abs
    1154              :  *  return the absolute value of the argument
    1155              :  ********************************************************************/
    1156              : 
    1157              : gnc_numeric
    1158        14102 : gnc_numeric_abs(gnc_numeric a)
    1159              : {
    1160        14102 :     if (gnc_numeric_check(a))
    1161              :     {
    1162            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1163              :     }
    1164        14102 :     return gnc_numeric_create(ABS(a.num), a.denom);
    1165              : }
    1166              : 
    1167              : 
    1168              : /* *******************************************************************
    1169              :  *  gnc_numeric_convert
    1170              :  ********************************************************************/
    1171              : 
    1172              : gnc_numeric
    1173        60694 : gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
    1174              : {
    1175        60694 :     if (gnc_numeric_check(in))
    1176            0 :         return in;
    1177              :     try
    1178              :     {
    1179        60694 :         return convert(GncNumeric(in), denom, how);
    1180              :     }
    1181            0 :     catch (const std::invalid_argument& err)
    1182              :     {
    1183            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1184            0 :     }
    1185            0 :     catch (const std::overflow_error& err)
    1186              :     {
    1187            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1188            0 :     }
    1189            0 :     catch (const std::underflow_error& err)
    1190              :     {
    1191            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1192            0 :     }
    1193            0 :     catch (const std::domain_error& err)
    1194              :     {
    1195            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1196            0 :     }
    1197              : }
    1198              : 
    1199              : 
    1200              : /* *******************************************************************
    1201              :  *  reduce a fraction by GCF elimination.  This is NOT done as a
    1202              :  *  part of the arithmetic API unless GNC_HOW_DENOM_REDUCE is specified
    1203              :  *  as the output denominator.
    1204              :  ********************************************************************/
    1205              : 
    1206              : gnc_numeric
    1207         1518 : gnc_numeric_reduce(gnc_numeric in)
    1208              : {
    1209         1518 :     if (gnc_numeric_check(in))
    1210              :     {
    1211            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1212              :     }
    1213              : 
    1214         1518 :     if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
    1215            0 :         return in;
    1216              :     try
    1217              :     {
    1218         1518 :         GncNumeric an (in);
    1219         1518 :         return static_cast<gnc_numeric>(an.reduce());
    1220              :     }
    1221            0 :     catch (const std::overflow_error& err)
    1222              :     {
    1223            0 :         PWARN("%s", err.what());
    1224            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1225            0 :     }
    1226            0 :     catch (const std::invalid_argument& err)
    1227              :     {
    1228            0 :         PWARN("%s", err.what());
    1229            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1230            0 :     }
    1231            0 :     catch (const std::underflow_error& err)
    1232              :     {
    1233            0 :         PWARN("%s", err.what());
    1234            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1235            0 :     }
    1236            0 :     catch (const std::domain_error& err)
    1237              :     {
    1238            0 :         PWARN("%s", err.what());
    1239            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1240            0 :     }
    1241              : }
    1242              : 
    1243              : 
    1244              : /* *******************************************************************
    1245              :  * gnc_numeric_to_decimal
    1246              :  *
    1247              :  * Attempt to convert the denominator to an exact power of ten without
    1248              :  * rounding. TRUE is returned if 'a' has been converted or was already
    1249              :  * decimal. Otherwise, FALSE is returned and 'a' remains unchanged.
    1250              :  * The 'max_decimal_places' parameter may be NULL.
    1251              :  ********************************************************************/
    1252              : 
    1253              : gboolean
    1254        23868 : gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
    1255              : {
    1256        23868 :     int max_places =  max_decimal_places == NULL ? max_leg_digits :
    1257              :         *max_decimal_places;
    1258        23868 :     if (a->num == 0) return TRUE;
    1259              :     try
    1260              :     {
    1261        14341 :         GncNumeric an (*a);
    1262        14341 :         auto bn = an.to_decimal(max_places);
    1263        14257 :         *a = static_cast<gnc_numeric>(bn);
    1264        14257 :         return TRUE;
    1265              :     }
    1266           84 :     catch (const std::exception& err)
    1267              :     {
    1268           84 :         PINFO ("%s", err.what());
    1269           84 :         return FALSE;
    1270           84 :     }
    1271              : }
    1272              : 
    1273              : 
    1274              : gnc_numeric
    1275          706 : gnc_numeric_invert(gnc_numeric num)
    1276              : {
    1277          706 :     if (num.num == 0)
    1278            0 :         return gnc_numeric_zero();
    1279              :     try
    1280              :     {
    1281          706 :         return static_cast<gnc_numeric>(GncNumeric(num).inv());
    1282              :     }
    1283            0 :     catch (const std::overflow_error& err)
    1284              :     {
    1285            0 :         PWARN("%s", err.what());
    1286            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1287            0 :     }
    1288            0 :     catch (const std::invalid_argument& err)
    1289              :     {
    1290            0 :         PWARN("%s", err.what());
    1291            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1292            0 :     }
    1293            0 :     catch (const std::underflow_error& err)
    1294              :     {
    1295            0 :         PWARN("%s", err.what());
    1296            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1297            0 :     }
    1298            0 :     catch (const std::domain_error& err)
    1299              :     {
    1300            0 :         PWARN("%s", err.what());
    1301            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1302            0 :     }
    1303              : }
    1304              : 
    1305              : /* *******************************************************************
    1306              :  *  double_to_gnc_numeric
    1307              :  ********************************************************************/
    1308              : 
    1309              : #ifdef _MSC_VER
    1310              : # define rint /* */
    1311              : #endif
    1312              : gnc_numeric
    1313          172 : double_to_gnc_numeric(double in, gint64 denom, gint how)
    1314              : {
    1315              :     try
    1316              :     {
    1317          172 :         GncNumeric an(in);
    1318          172 :         return convert(an, denom, how);
    1319              :     }
    1320            0 :     catch (const std::overflow_error& err)
    1321              :     {
    1322            0 :         PWARN("%s", err.what());
    1323            0 :         return gnc_numeric_error(GNC_ERROR_OVERFLOW);
    1324            0 :     }
    1325            0 :     catch (const std::invalid_argument& err)
    1326              :     {
    1327            0 :         PWARN("%s", err.what());
    1328            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1329            0 :     }
    1330            0 :     catch (const std::underflow_error& err)
    1331              :     {
    1332            0 :         PWARN("%s", err.what());
    1333            0 :         return gnc_numeric_error(GNC_ERROR_ARG);
    1334            0 :     }
    1335            0 :     catch (const std::domain_error& err)
    1336              :     {
    1337            0 :         PWARN("%s", err.what());
    1338            0 :         return gnc_numeric_error(GNC_ERROR_REMAINDER);
    1339            0 :     }
    1340              : }
    1341              : 
    1342              : /* *******************************************************************
    1343              :  *  gnc_numeric_to_double
    1344              :  ********************************************************************/
    1345              : 
    1346              : double
    1347          205 : gnc_numeric_to_double(gnc_numeric in)
    1348              : {
    1349          205 :     if (in.denom > 0)
    1350              :     {
    1351          180 :         return (double)in.num / (double)in.denom;
    1352              :     }
    1353              :     else
    1354              :     {
    1355           25 :         return (double)(in.num * -in.denom);
    1356              :     }
    1357              : }
    1358              : 
    1359              : /* *******************************************************************
    1360              :  *  gnc_numeric_error
    1361              :  ********************************************************************/
    1362              : 
    1363              : gnc_numeric
    1364          536 : gnc_numeric_error(GNCNumericErrorCode error_code)
    1365              : {
    1366          536 :     return gnc_numeric_create(error_code, 0LL);
    1367              : }
    1368              : 
    1369              : 
    1370              : 
    1371              : /* *******************************************************************
    1372              :  *  gnc_numeric text IO
    1373              :  ********************************************************************/
    1374              : 
    1375              : gchar *
    1376         8134 : gnc_numeric_to_string(gnc_numeric n)
    1377              : {
    1378              :     char *result;
    1379         8134 :     int64_t tmpnum = n.num;
    1380         8134 :     int64_t tmpdenom = n.denom;
    1381              : 
    1382         8134 :     result = g_strdup_printf("%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
    1383              : 
    1384         8134 :     return result;
    1385              : }
    1386              : 
    1387              : gchar *
    1388           36 : gnc_num_dbg_to_string(gnc_numeric n)
    1389              : {
    1390              :     static char buff[1000];
    1391              :     static char *p = buff;
    1392              :     static const size_t size = 50;
    1393           36 :     int64_t tmpnum = n.num;
    1394           36 :     int64_t tmpdenom = n.denom;
    1395              : 
    1396           36 :     p += size;
    1397           36 :     if ((size_t)(p - buff) > (sizeof(buff) - size))
    1398            1 :         p = buff;
    1399              : 
    1400           36 :     snprintf(p, size, "%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
    1401              : 
    1402           36 :     return p;
    1403              : }
    1404              : 
    1405              : gnc_numeric
    1406         5177 : gnc_numeric_from_string (const gchar* str)
    1407              : {
    1408         5177 :     if (!str)
    1409            0 :         return gnc_numeric_error (GNC_ERROR_ARG);
    1410              : 
    1411              :     // the default gnc_numeric string format is "num/denom", whereby
    1412              :     // the denom must be >= 1. this speedily parses it. this also
    1413              :     // parses "num" as num/1.
    1414         5177 :     if (auto res = fast_numeral_rational (str))
    1415         5168 :         return *res;
    1416              : 
    1417              :     try
    1418              :     {
    1419           18 :         return GncNumeric (str);
    1420              :     }
    1421            3 :     catch (const std::exception& err)
    1422              :     {
    1423            3 :         PWARN("%s", err.what());
    1424            3 :         return gnc_numeric_error (GNC_ERROR_ARG);
    1425            3 :     }
    1426              : }
    1427              : 
    1428              : /* *******************************************************************
    1429              :  *  GValue handling
    1430              :  ********************************************************************/
    1431              : static gnc_numeric*
    1432          137 : gnc_numeric_boxed_copy_func( gnc_numeric *in_gnc_numeric )
    1433              : {
    1434          137 :     if (!in_gnc_numeric)
    1435            0 :         return nullptr;
    1436              : 
    1437              :     /* newvalue will be passed to g_free so we must allocate with g_malloc. */
    1438          137 :     auto newvalue = static_cast<gnc_numeric*>(g_malloc (sizeof (gnc_numeric)));
    1439          137 :     *newvalue = *in_gnc_numeric;
    1440              : 
    1441          137 :     return newvalue;
    1442              : }
    1443              : 
    1444              : static void
    1445           59 : gnc_numeric_boxed_free_func( gnc_numeric *in_gnc_numeric )
    1446              : {
    1447           59 :     g_free( in_gnc_numeric );
    1448           59 : }
    1449              : 
    1450         1271 : G_DEFINE_BOXED_TYPE (gnc_numeric, gnc_numeric, gnc_numeric_boxed_copy_func, gnc_numeric_boxed_free_func)
    1451              : 
    1452              : /* *******************************************************************
    1453              :  *  gnc_numeric misc testing
    1454              :  ********************************************************************/
    1455              : #ifdef _GNC_NUMERIC_TEST
    1456              : 
    1457              : static char *
    1458              : gnc_numeric_print(gnc_numeric in)
    1459              : {
    1460              :     char * retval;
    1461              :     if (gnc_numeric_check(in))
    1462              :     {
    1463              :         retval = g_strdup_printf("<ERROR> [%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
    1464              :                                  in.num,
    1465              :                                  in.denom);
    1466              :     }
    1467              :     else
    1468              :     {
    1469              :         retval = g_strdup_printf("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
    1470              :                                  in.num,
    1471              :                                  in.denom);
    1472              :     }
    1473              :     return retval;
    1474              : }
    1475              : 
    1476              : int
    1477              : main(int argc, char ** argv)
    1478              : {
    1479              :     gnc_numeric a = gnc_numeric_create(1, 3);
    1480              :     gnc_numeric b = gnc_numeric_create(1, 4);
    1481              :     gnc_numeric c;
    1482              : 
    1483              :     gnc_numeric err;
    1484              : 
    1485              : 
    1486              :     printf("multiply (EXACT): %s * %s = %s\n",
    1487              :            gnc_numeric_print(a), gnc_numeric_print(b),
    1488              :            gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT)));
    1489              : 
    1490              :     printf("multiply (REDUCE): %s * %s = %s\n",
    1491              :            gnc_numeric_print(a), gnc_numeric_print(b),
    1492              :            gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE)));
    1493              : 
    1494              : 
    1495              :     return 0;
    1496              : }
    1497              : #endif
    1498              : 
    1499              : 
    1500              : std::ostream&
    1501          284 : operator<<(std::ostream& s, GncNumeric n)
    1502              : {
    1503              :     using boost::locale::conv::utf_to_utf;
    1504          284 :     std::basic_ostringstream<wchar_t> ss;
    1505          284 :     ss.imbue(s.getloc());
    1506          284 :     ss << n;
    1507          284 :     s << utf_to_utf<char>(ss.str());
    1508          284 :     return s;
    1509          284 : }
    1510              : 
    1511            8 : const char* gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
    1512              : {
    1513            8 :     switch (error_code)
    1514              :     {
    1515            6 :     case GNC_ERROR_OK:
    1516            6 :         return "GNC_ERROR_OK";
    1517            2 :     case GNC_ERROR_ARG:
    1518            2 :         return "GNC_ERROR_ARG";
    1519            0 :     case GNC_ERROR_OVERFLOW:
    1520            0 :         return "GNC_ERROR_OVERFLOW";
    1521            0 :     case GNC_ERROR_DENOM_DIFF:
    1522            0 :         return "GNC_ERROR_DENOM_DIFF";
    1523            0 :     case GNC_ERROR_REMAINDER:
    1524            0 :         return "GNC_ERROR_REMAINDER";
    1525            0 :     default:
    1526            0 :         return "<unknown>";
    1527              :     }
    1528              : }
    1529              : 
    1530              : /* ======================== END OF FILE =================== */
        

Generated by: LCOV version 2.0-1