LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-rational.hpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 59.1 % 22 13
Test Date: 2025-02-07 16:25:45 Functions: 45.0 % 20 9
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /********************************************************************
       2                 :             :  * gnc-rational.hpp - A rational number library                     *
       3                 :             :  * Copyright 2014 John Ralls <jralls@ceridwen.us>                   *
       4                 :             :  * This program is free software; you can redistribute it and/or    *
       5                 :             :  * modify it under the terms of the GNU General Public License as   *
       6                 :             :  * published by the Free Software Foundation; either version 2 of   *
       7                 :             :  * the License, or (at your option) any later version.              *
       8                 :             :  *                                                                  *
       9                 :             :  * This program is distributed in the hope that it will be useful,  *
      10                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      11                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      12                 :             :  * GNU General Public License for more details.                     *
      13                 :             :  *                                                                  *
      14                 :             :  * You should have received a copy of the GNU General Public License*
      15                 :             :  * along with this program; if not, contact:                        *
      16                 :             :  *                                                                  *
      17                 :             :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      18                 :             :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      19                 :             :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      20                 :             :  *                                                                  *
      21                 :             :  *******************************************************************/
      22                 :             : 
      23                 :             : #ifndef __GNC_RATIONAL_HPP__
      24                 :             : #define __GNC_RATIONAL_HPP__
      25                 :             : 
      26                 :             : #include "gnc-numeric.h"
      27                 :             : #include "gnc-int128.hpp"
      28                 :             : #include "gnc-rational-rounding.hpp"
      29                 :             : 
      30                 :             : class GncNumeric;
      31                 :             : enum class RoundType;
      32                 :             : enum class DenomType;
      33                 :             : 
      34                 :             : /** @ingroup QOF
      35                 :             :  *  @brief Rational number class using GncInt128 for the numerator
      36                 :             :  *  and denominator.
      37                 :             :  *
      38                 :             :  * This class provides far greater overflow protection compared to GncNumeric at
      39                 :             :  * the expense of doubling the size, so GncNumeric is preferred for storage into
      40                 :             :  * objects. Furthermore the backends are not able to store GncRational numbers;
      41                 :             :  * storage in SQL would require using BLOBs which would preclude calculations in
      42                 :             :  * queries. GncRational exists *primarily* as a more overflow-resistant
      43                 :             :  * calculation facility for GncNumeric. It's available for cases where one needs
      44                 :             :  * an error instead of an automatically rounded value for a calculation that
      45                 :             :  * produces a result that won't fit into an int64 without rounding.
      46                 :             : 
      47                 :             :  * Errors: Errors are signalled by exceptions as follows:
      48                 :             :  * * A zero denominator will raise a std::invalid_argument.
      49                 :             :  * * Division by zero will raise a std::underflow_error.
      50                 :             :  * * Overflowing 128 bits will raise a std::overflow_error.
      51                 :             :  * * Failure to convert a number as specified by the arguments to convert() will
      52                 :             :  * raise a std::domain_error.
      53                 :             :  *
      54                 :             :  */
      55                 :             : 
      56                 :             : 
      57                 :             : class GncRational
      58                 :             : {
      59                 :             : public:
      60                 :             :     /**
      61                 :             :      * Default constructor provides the zero value.
      62                 :             :      */
      63                 :        3311 :     GncRational() : m_num(0), m_den(1) {}
      64                 :             :     /**
      65                 :             :      * GncInt128 constructor. This will take any flavor of built-in integer
      66                 :             :      * thanks to implicit construction of the GncInt128s.
      67                 :             :      */
      68                 :      468834 :     GncRational (GncInt128 num, GncInt128 den) noexcept
      69                 :      468834 :         : m_num(num), m_den(den) {}
      70                 :             :     /** Convenience constructor from the C API's gnc_numeric. */
      71                 :             :     GncRational (gnc_numeric n) noexcept;
      72                 :             :     /** GncNumeric constructor. */
      73                 :             :     GncRational(GncNumeric n) noexcept;
      74                 :             :     GncRational(const GncRational& rhs) = default;
      75                 :             :     GncRational(GncRational&& rhs) = default;
      76                 :             :     GncRational& operator=(const GncRational& rhs) = default;
      77                 :             :     GncRational& operator=(GncRational&& rhs) = default;
      78                 :             :     ~GncRational() = default;
      79                 :             :     /** Report if both members are valid numbers.
      80                 :             :      * \return true if neither numerator nor denominator are Nan or Overflowed.
      81                 :             :      */
      82                 :             :     bool valid() const noexcept;
      83                 :             :     /** Report if either numerator or denominator are too big to fit in an
      84                 :             :      * int64_t.
      85                 :             :      * \return true if either is too big.
      86                 :             :      */
      87                 :             :     bool is_big() const noexcept;
      88                 :             :     /** Conversion operator; use static_cast<gnc_numeric>(foo). */
      89                 :             :     operator gnc_numeric() const noexcept;
      90                 :             :     /** Make a new GncRational with the opposite sign. */
      91                 :             :     GncRational operator-() const noexcept;
      92                 :             :     /**
      93                 :             :      * Return an equivalent fraction with all common factors between the
      94                 :             :      * numerator and the denominator removed.
      95                 :             :      *
      96                 :             :      * @return reduced GncRational
      97                 :             :      */
      98                 :             :     GncRational reduce() const;
      99                 :             :     /**
     100                 :             :      * Round to fit an int64_t, finding the closest possible approximation.
     101                 :             :      *
     102                 :             :      * Throws std::overflow_error if m_den is 1 and m_num is big.
     103                 :             :      * @return rounded GncRational
     104                 :             :      */
     105                 :             :     GncRational round_to_numeric() const;
     106                 :             :     /**
     107                 :             :      * Convert a GncRational to use a new denominator. If rounding is necessary
     108                 :             :      * use the indicated template specification. For example, to use half-up
     109                 :             :      * rounding you'd call bar = foo.convert<RoundType::half_up>(1000). If you
     110                 :             :      * specify RoundType::never this will throw std::domain_error if rounding is
     111                 :             :      * required.
     112                 :             :      *
     113                 :             :      * \param new_denom The new denominator to convert the fraction to.
     114                 :             :      * \return A new GncRational having the requested denominator.
     115                 :             :      */
     116                 :             :     template <RoundType RT>
     117                 :        6141 :     GncRational convert (GncInt128 new_denom) const
     118                 :             :     {
     119                 :        6141 :         auto params = prepare_conversion(new_denom);
     120                 :        6141 :         if (new_denom == GNC_DENOM_AUTO)
     121                 :         443 :             new_denom = m_den;
     122                 :        6141 :         if (params.rem == 0)
     123                 :        4959 :             return GncRational(params.num, new_denom);
     124                 :        1182 :         return GncRational(round(params.num, params.den,
     125                 :        1182 :                                  params.rem, RT2T<RT>()), new_denom);
     126                 :             :     }
     127                 :             : 
     128                 :             :     /**
     129                 :             :      * Convert with the specified sigfigs. The resulting denominator depends on
     130                 :             :      * the value of the GncRational, such that the specified significant digits
     131                 :             :      * are retained in the numerator and the denominator is always a power of
     132                 :             :      * 10. This is of rather dubious benefit in an accounting program, but it's
     133                 :             :      * used in several places so it needs to be implemented.
     134                 :             :      *
     135                 :             :      * @param figs The number of digits to use for the numerator.
     136                 :             :      * @return A GncRational with the specified number of digits in the
     137                 :             :      * numerator and the appropriate power-of-ten denominator.
     138                 :             :      */
     139                 :             :     template <RoundType RT>
     140                 :           0 :     GncRational convert_sigfigs(unsigned int figs) const
     141                 :             :     {
     142                 :           0 :         auto new_denom(sigfigs_denom(figs));
     143                 :           0 :         auto params = prepare_conversion(new_denom);
     144                 :           0 :         if (new_denom == 0) //It had better not, but just in case...
     145                 :           0 :             new_denom = 1;
     146                 :           0 :         if (params.rem == 0)
     147                 :           0 :             return GncRational(params.num, new_denom);
     148                 :           0 :         return GncRational(round(params.num, params.den,
     149                 :           0 :                                 params.rem, RT2T<RT>()), new_denom);
     150                 :             :     }
     151                 :             : 
     152                 :             :     /** Numerator accessor */
     153                 :     1656352 :     GncInt128 num() const noexcept { return m_num; }
     154                 :             :     /** Denominator accessor */
     155                 :     2387649 :     GncInt128 denom() const noexcept { return m_den; }
     156                 :             :     /** @defgroup gnc_rational_mutators
     157                 :             :      *  @{
     158                 :             :      * Standard mutating arithmetic operators.
     159                 :             :      */
     160                 :             :     void operator+=(GncRational b);
     161                 :             :     void operator-=(GncRational b);
     162                 :             :     void operator*=(GncRational b);
     163                 :             :     void operator/=(GncRational b);
     164                 :             :     /** @} */
     165                 :             :     /** Inverts the number, equivalent of /= {1, 1} */
     166                 :             :     GncRational inv() const noexcept;
     167                 :             :     /** Absolute value; return value is always >= 0 and of same magnitude. */
     168                 :             :     GncRational abs() const noexcept;
     169                 :             :     /** Compare function
     170                 :             :      *
     171                 :             :      * @param b GncNumeric or integer value to compare to.
     172                 :             :      * @return -1 if < b, 0 if equal, 1 if > b.
     173                 :             :      */
     174                 :             :     int cmp(GncRational b);
     175                 :             :     int cmp(GncInt128 b) { return cmp(GncRational(b, 1)); }
     176                 :             : 
     177                 :             : private:
     178                 :             :     struct round_param
     179                 :             :     {
     180                 :             :         GncInt128 num;
     181                 :             :         GncInt128 den;
     182                 :             :         GncInt128 rem;
     183                 :             :     };
     184                 :             :     /* Calculates the denominator required to convert to figs sigfigs. Note that
     185                 :             :      * it uses the same powten function that the GncNumeric version does because
     186                 :             :      * 17 significant figures should be plenty.
     187                 :             :      */
     188                 :             :     GncInt128 sigfigs_denom(unsigned figs) const noexcept;
     189                 :             :     /* Calculates a round_param struct to pass to a rounding function that will
     190                 :             :      * finish computing a GncNumeric with the new denominator.
     191                 :             :      */
     192                 :             :     round_param prepare_conversion(GncInt128 new_denom) const;
     193                 :             :     GncInt128 m_num;
     194                 :             :     GncInt128 m_den;
     195                 :             : };
     196                 :             : 
     197                 :             : /**
     198                 :             :  * @return -1 if a < b, 0 if a == b, 1 if a > b.
     199                 :             :  */
     200                 :             : inline int cmp(GncRational a, GncRational b) { return a.cmp(b); }
     201                 :             : inline int cmp(GncRational a, GncInt128 b) { return a.cmp(b); }
     202                 :             : inline int cmp(GncInt128 a, GncRational b) { return GncRational(a, 1).cmp(b); }
     203                 :             : 
     204                 :             : /**
     205                 :             :  * \defgroup gnc_rational_comparison_operators
     206                 :             :  * @{
     207                 :             :  * Standard comparison operators, which do what one would expect.
     208                 :             :  */
     209                 :             : inline bool operator<(GncRational a, GncRational b) { return cmp(a, b) < 0; }
     210                 :             : inline bool operator<(GncRational a, GncInt128 b) { return cmp(a, b) < 0; }
     211                 :             : inline bool operator<(GncInt128 a, GncRational b) { return cmp(a, b) < 0; }
     212                 :             : inline bool operator>(GncRational a, GncRational b) { return cmp(a, b) > 0; }
     213                 :             : inline bool operator>(GncRational a, GncInt128 b) { return cmp(a, b) > 0; }
     214                 :             : inline bool operator>(GncInt128 a, GncRational b) { return cmp(a, b) > 0; }
     215                 :             : inline bool operator==(GncRational a, GncRational b) { return cmp(a, b) == 0; }
     216                 :             : inline bool operator==(GncRational a, GncInt128 b) { return cmp(a, b) == 0; }
     217                 :             : inline bool operator==(GncInt128 a, GncRational b) { return cmp(a, b) == 0; }
     218                 :             : inline bool operator<=(GncRational a, GncRational b) { return cmp(a, b) <= 0; }
     219                 :             : inline bool operator<=(GncRational a, GncInt128 b) { return cmp(a, b) <= 0; }
     220                 :             : inline bool operator<=(GncInt128 a, GncRational b) { return cmp(a, b) <= 0; }
     221                 :             : inline bool operator>=(GncRational a, GncRational b) { return cmp(a, b) >= 0; }
     222                 :             : inline bool operator>=(GncRational a, GncInt128 b) { return cmp(a, b) >= 0; }
     223                 :             : inline bool operator>=(GncInt128 a, GncRational b) { return cmp(a, b) >= 0; }
     224                 :             : inline bool operator!=(GncRational a, GncRational b) { return cmp(a, b) != 0; }
     225                 :             : inline bool operator!=(GncRational a, GncInt128 b) { return cmp(a, b) != 0; }
     226                 :             : inline bool operator!=(GncInt128 a, GncRational b) { return cmp(a, b) != 0; }
     227                 :             : /** @} */
     228                 :             : 
     229                 :             : /**
     230                 :             :  * \defgroup gnc_rational_arithmetic_operators
     231                 :             :  *
     232                 :             :  * Normal arithmetic operators. The class arithmetic operators are implemented
     233                 :             :  * in terms of these operators.
     234                 :             :  *
     235                 :             :  * These operators can throw std::overflow_error, std::underflow_error, or
     236                 :             :  * std::invalid argument as indicated in the class documentation.
     237                 :             :  *
     238                 :             :  * \param a The right-side operand
     239                 :             :  * \param b The left-side operand
     240                 :             :  * \return A GncRational computed from the operation.
     241                 :             :  */
     242                 :             : GncRational operator+(GncRational a, GncRational b);
     243                 :             : inline GncRational operator+(GncRational a, GncInt128 b)
     244                 :             : {
     245                 :             :     return a + GncRational(b, 1);
     246                 :             : }
     247                 :             : inline GncRational operator+(GncInt128 a, GncRational b)
     248                 :             : {
     249                 :             :     return GncRational(a, 1) + b;
     250                 :             : }
     251                 :             : GncRational operator-(GncRational a, GncRational b);
     252                 :             : inline GncRational operator-(GncRational a, GncInt128 b)
     253                 :             : {
     254                 :             :     return a - GncRational(b, 1);
     255                 :             : }
     256                 :             : inline GncRational operator-(GncInt128 a, GncRational b)
     257                 :             : {
     258                 :             :     return GncRational(a, 1) - b;
     259                 :             : }
     260                 :             : GncRational operator*(GncRational a, GncRational b);
     261                 :             : inline GncRational operator*(GncRational a, GncInt128 b)
     262                 :             : {
     263                 :             :     return a * GncRational(b, 1);
     264                 :             : }
     265                 :             : inline GncRational operator*(GncInt128 a, GncRational b)
     266                 :             : {
     267                 :             :     return GncRational(a, 1) * b;
     268                 :             : }
     269                 :             : GncRational operator/(GncRational a, GncRational b);
     270                 :             : inline GncRational operator/(GncRational a, GncInt128 b)
     271                 :             : {
     272                 :             :     return a / GncRational(b, 1);
     273                 :             : }
     274                 :             : inline GncRational operator/(GncInt128 a, GncRational b)
     275                 :             : {
     276                 :             :     return GncRational(a, 1) / b;
     277                 :             : }
     278                 :             : 
     279                 :             : inline std::ostream& operator<<(std::ostream& stream, const GncRational& val) noexcept
     280                 :             : {
     281                 :             :     stream << val.num() << "/" << val.denom();
     282                 :             :     return stream;
     283                 :             : }
     284                 :             : /** @} */
     285                 :             : #endif //__GNC_RATIONAL_HPP__
        

Generated by: LCOV version 2.0-1