LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-rational-rounding.hpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 53.5 % 86 46
Test Date: 2026-11-02 18:19:43 Functions: 64.7 % 17 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************
       2              :  * gnc-rational-rounding.hpp - Template functions for rounding      *
       3              :  * Copyright 2017 John Ralls <jralls@ceridwen.us>                   *
       4              :  * This program is free software; you can redistribute it and/or    *
       5              :  * modify it under the terms of the GNU General Public License as   *
       6              :  * published by the Free Software Foundation; either version 2 of   *
       7              :  * the License, or (at your option) any later version.              *
       8              :  *                                                                  *
       9              :  * This program is distributed in the hope that it will be useful,  *
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      12              :  * GNU General Public License for more details.                     *
      13              :  *                                                                  *
      14              :  * You should have received a copy of the GNU General Public License*
      15              :  * along with this program; if not, contact:                        *
      16              :  *                                                                  *
      17              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      18              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      19              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      20              :  *                                                                  *
      21              :  *******************************************************************/
      22              : 
      23              : #ifndef __GNC_RATIONAL_ROUNDING_HPP__
      24              : #define __GNC_RATIONAL_ROUNDING_HPP__
      25              : #include "gnc-numeric.h"
      26              : #include "gnc-int128.hpp"
      27              : 
      28              : template <typename T> inline bool
      29           10 : quotient_is_positive(T dividend, T divisor)
      30              : {
      31           10 :     return (dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0);
      32              : }
      33              : 
      34              : enum class RoundType
      35              : {
      36              :     floor = GNC_HOW_RND_FLOOR,
      37              :     ceiling = GNC_HOW_RND_CEIL,
      38              :     truncate = GNC_HOW_RND_TRUNC,
      39              :     promote = GNC_HOW_RND_PROMOTE,
      40              :     half_down = GNC_HOW_RND_ROUND_HALF_DOWN,
      41              :     half_up = GNC_HOW_RND_ROUND_HALF_UP,
      42              :     bankers = GNC_HOW_RND_ROUND,
      43              :     never = GNC_HOW_RND_NEVER,
      44              : };
      45              : 
      46              : enum class DenomType
      47              : {
      48              :     den_auto = GNC_DENOM_AUTO,
      49              :     exact = GNC_HOW_DENOM_EXACT,
      50              :     reduce = GNC_HOW_DENOM_REDUCE,
      51              :     lcd = GNC_HOW_DENOM_LCD,
      52              :     fixed = GNC_HOW_DENOM_FIXED,
      53              :     sigfigs = GNC_HOW_DENOM_SIGFIG,
      54              : };
      55              : 
      56              : 
      57              : template <RoundType rt>
      58              : struct RT2T
      59              : {
      60              :     RoundType value = rt;
      61              : };
      62              : 
      63              : /* The following templates implement the rounding policies for the convert and
      64              :  * convert_sigfigs template functions.
      65              :  */
      66              : template <typename T> inline T
      67           84 : round(T num, T den, T rem, RT2T<RoundType::never>)
      68              : {
      69           84 :     if (rem == 0)
      70            0 :         return num;
      71           84 :     throw std::domain_error("Rounding required when 'never round' specified.");
      72              : }
      73              : 
      74              : template <typename T> inline T
      75           11 : round(T num, T den, T rem, RT2T<RoundType::floor>)
      76              : {
      77              : //    std::cout << "Rounding to floor  with num " << num << " den " << den
      78              : //              << ", and rem " << rem << ".\n";
      79           11 :     if (rem == 0)
      80            0 :         return num;
      81              :     // floor num==0 that is the quotient of two numbers with opposite signs
      82           11 :     if (num < 0 || (num == 0 && !quotient_is_positive(rem, den)))
      83            6 :         return num - 1;
      84            5 :     return num;
      85              : }
      86              : 
      87              : 
      88              : template <> inline GncInt128
      89            0 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
      90              :                  RT2T<RoundType::floor>)
      91              : {
      92              : //    std::cout << "Rounding to floor  with num " << num << " den " << den
      93              : //              << ", and rem " << rem << ".\n";
      94            0 :     if (rem == 0)
      95            0 :         return num;
      96            0 :     if (num.isNeg())
      97            0 :         return num - 1;
      98            0 :     return num;
      99              : }
     100              : 
     101              : template <typename T> inline T
     102            1 : round(T num, T den, T rem, RT2T<RoundType::ceiling>)
     103              : {
     104            1 :     if (rem == 0)
     105            0 :         return num;
     106            1 :     if (num > 0 || (num == 0 && quotient_is_positive(rem, den)))
     107            1 :         return num + 1;
     108            0 :     return num;
     109              : }
     110              : 
     111              : template <> inline GncInt128
     112            0 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
     113              :       RT2T<RoundType::ceiling>)
     114              : {
     115            0 :     if (rem == 0)
     116            0 :         return num;
     117            0 :     if (!num.isNeg())
     118            0 :         return num + 1;
     119            0 :     return num;
     120              : }
     121              : 
     122              : template <typename T> inline T
     123         4388 : round(T num, T den, T rem, RT2T<RoundType::truncate>)
     124              : {
     125         4388 :     return num;
     126              : }
     127              : 
     128              : template <typename T> inline T
     129            0 : round(T num, T den, T rem, RT2T<RoundType::promote>)
     130              : {
     131            0 :     if (rem == 0)
     132            0 :         return num;
     133            0 :     if (num == 0)
     134            0 :         return (!quotient_is_positive(rem, den) ? -1 : 1);
     135            0 :     return num + (num < 0 ? -1 : 1);
     136              : }
     137              : 
     138              : template <> inline GncInt128
     139            0 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
     140              :                  RT2T<RoundType::promote>)
     141              : {
     142            0 :     if (rem == 0)
     143            0 :         return num;
     144            0 :     return num + (num.isNeg() ? -1 : 1);
     145              : }
     146              : 
     147              : template <typename T> inline T
     148            0 : round(T num, T den, T rem, RT2T<RoundType::half_down>)
     149              : {
     150            0 :     if (rem == 0)
     151            0 :         return num;
     152            0 :     if (std::abs(rem * 2) > std::abs(den))
     153              :     {
     154            0 :         if (num == 0)
     155            0 :             return (!quotient_is_positive(rem, den) ? -1 : 1);
     156            0 :         return num + (num < 0 ? -1 : 1);
     157              :     }
     158            0 :     return num;
     159              : }
     160              : 
     161              : template <> inline GncInt128
     162           57 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
     163              :                  RT2T<RoundType::half_down>)
     164              : {
     165           57 :     if (rem == 0)
     166            0 :         return num;
     167           57 :     if (rem.abs() * 2 > den.abs())
     168           23 :         return num + (num.isNeg() ? -1 : 1);
     169           34 :     return num;
     170              : }
     171              : 
     172              : template <typename T> inline T
     173         1424 : round(T num, T den, T rem, RT2T<RoundType::half_up>)
     174              : {
     175         1424 :     if (rem == 0)
     176            0 :         return num;
     177         1424 :     if (std::abs(rem) * 2 >= std::abs(den))
     178              :     {
     179          725 :         if (num == 0)
     180            9 :             return (!quotient_is_positive(rem, den) ? -1 : 1);
     181          716 :         return num + (num < 0 ? -1 : 1);
     182              :     }
     183          699 :     return num;
     184              : }
     185              : 
     186              : template <> inline GncInt128
     187           63 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
     188              :                  RT2T<RoundType::half_up>)
     189              : {
     190           63 :     if (rem == 0)
     191            0 :         return num;
     192           63 :     if (rem.abs() * 2 >= den.abs())
     193           27 :         return num + (num.isNeg() ? -1 : 1);
     194           36 :     return num;
     195              : }
     196              : 
     197              : template <typename T> inline T
     198          301 : round(T num, T den, T rem, RT2T<RoundType::bankers>)
     199              : {
     200          301 :     if (rem == 0)
     201            0 :         return num;
     202          436 :     if (std::abs(rem * 2) > std::abs(den) ||
     203          135 :         (std::abs(rem * 2) == std::abs(den) && num % 2))
     204              :     {
     205          194 :         if (num == 0)
     206            0 :             return (!quotient_is_positive(rem, den) ? -1 : 1);
     207          194 :         return num + (num < 0 ? -1 : 1);
     208              :     }
     209          107 :     return num;
     210              : }
     211              : 
     212              : template <> inline GncInt128
     213          462 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
     214              :                  RT2T<RoundType::bankers>)
     215              : {
     216          462 :     if (rem == 0)
     217            0 :         return num;
     218          519 :     if (rem.abs() * 2 > den.abs() ||
     219          519 :         (rem.abs() * 2 == den.abs() && num % 2))
     220          405 :         return num + (num.isNeg() ? -1 : 1);
     221           57 :     return num;
     222              : }
     223              : #endif //__GNC_RATIONAL_ROUNDING_HPP__
        

Generated by: LCOV version 2.0-1