GnuCash  5.6-150-g038405b370+
gnc-rational-rounding.hpp
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 quotient_is_positive(T dividend, T divisor)
30 {
31  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 round(T num, T den, T rem, RT2T<RoundType::never>)
68 {
69  if (rem == 0)
70  return num;
71  throw std::domain_error("Rounding required when 'never round' specified.");
72 }
73 
74 template <typename T> inline T
75 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  if (rem == 0)
80  return num;
81  // floor num==0 that is the quotient of two numbers with opposite signs
82  if (num < 0 || (num == 0 && !quotient_is_positive(rem, den)))
83  return num - 1;
84  return num;
85 }
86 
87 
88 template <> inline GncInt128
89 round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
91 {
92 // std::cout << "Rounding to floor with num " << num << " den " << den
93 // << ", and rem " << rem << ".\n";
94  if (rem == 0)
95  return num;
96  if (num.isNeg())
97  return num - 1;
98  return num;
99 }
100 
101 template <typename T> inline T
102 round(T num, T den, T rem, RT2T<RoundType::ceiling>)
103 {
104  if (rem == 0)
105  return num;
106  if (num > 0 || (num == 0 && quotient_is_positive(rem, den)))
107  return num + 1;
108  return num;
109 }
110 
111 template <> inline GncInt128
112 round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
114 {
115  if (rem == 0)
116  return num;
117  if (!num.isNeg())
118  return num + 1;
119  return num;
120 }
121 
122 template <typename T> inline T
123 round(T num, T den, T rem, RT2T<RoundType::truncate>)
124 {
125  return num;
126 }
127 
128 template <typename T> inline T
129 round(T num, T den, T rem, RT2T<RoundType::promote>)
130 {
131  if (rem == 0)
132  return num;
133  if (num == 0)
134  return (!quotient_is_positive(rem, den) ? -1 : 1);
135  return num + (num < 0 ? -1 : 1);
136 }
137 
138 template <> inline GncInt128
139 round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
141 {
142  if (rem == 0)
143  return num;
144  return num + (num.isNeg() ? -1 : 1);
145 }
146 
147 template <typename T> inline T
148 round(T num, T den, T rem, RT2T<RoundType::half_down>)
149 {
150  if (rem == 0)
151  return num;
152  if (std::abs(rem * 2) > std::abs(den))
153  {
154  if (num == 0)
155  return (!quotient_is_positive(rem, den) ? -1 : 1);
156  return num + (num < 0 ? -1 : 1);
157  }
158  return num;
159 }
160 
161 template <> inline GncInt128
162 round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
164 {
165  if (rem == 0)
166  return num;
167  if (rem.abs() * 2 > den.abs())
168  return num + (num.isNeg() ? -1 : 1);
169  return num;
170 }
171 
172 template <typename T> inline T
173 round(T num, T den, T rem, RT2T<RoundType::half_up>)
174 {
175  if (rem == 0)
176  return num;
177  if (std::abs(rem) * 2 >= std::abs(den))
178  {
179  if (num == 0)
180  return (!quotient_is_positive(rem, den) ? -1 : 1);
181  return num + (num < 0 ? -1 : 1);
182  }
183  return num;
184 }
185 
186 template <> inline GncInt128
187 round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
189 {
190  if (rem == 0)
191  return num;
192  if (rem.abs() * 2 >= den.abs())
193  return num + (num.isNeg() ? -1 : 1);
194  return num;
195 }
196 
197 template <typename T> inline T
198 round(T num, T den, T rem, RT2T<RoundType::bankers>)
199 {
200  if (rem == 0)
201  return num;
202  if (std::abs(rem * 2) > std::abs(den) ||
203  (std::abs(rem * 2) == std::abs(den) && num % 2))
204  {
205  if (num == 0)
206  return (!quotient_is_positive(rem, den) ? -1 : 1);
207  return num + (num < 0 ? -1 : 1);
208  }
209  return num;
210 }
211 
212 template <> inline GncInt128
213 round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
215 {
216  if (rem == 0)
217  return num;
218  if (rem.abs() * 2 > den.abs() ||
219  (rem.abs() * 2 == den.abs() && num % 2))
220  return num + (num.isNeg() ? -1 : 1);
221  return num;
222 }
223 #endif //__GNC_RATIONAL_ROUNDING_HPP__
Never round at all, and signal an error if there is a fractional result in a computation.
Definition: gnc-numeric.h:177
Round toward +infinity.
Definition: gnc-numeric.h:149
An exact-rational-number library for gnucash.
All arguments are required to have the same denominator, that denominator is to be used in the output...
Definition: gnc-numeric.h:206
Use any denominator which gives an exactly correct ratio of numerator to denominator.
Definition: gnc-numeric.h:188
Round toward -infinity.
Definition: gnc-numeric.h:146
Find the least common multiple of the arguments&#39; denominators and use that as the denominator of the ...
Definition: gnc-numeric.h:200
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
Definition: gnc-numeric.h:195
Truncate fractions (round toward zero)
Definition: gnc-numeric.h:152
Promote fractions (round away from zero)
Definition: gnc-numeric.h:155
Round to the nearest integer, rounding toward zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:160
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
Use unbiased ("banker&#39;s") rounding.
Definition: gnc-numeric.h:172
Round to the number of significant figures given in the rounding instructions by the GNC_HOW_DENOM_SI...
Definition: gnc-numeric.h:211
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245