"Fossies" - the Fresh Open Source Software Archive

Member "gnucash-3.7/libgnucash/engine/gnc-numeric.cpp" (7 Sep 2019, 37808 Bytes) of package /linux/misc/gnucash-3.7.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gnc-numeric.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.6_vs_3.7.

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