Branch data 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 : 7 : quotient_is_positive(T dividend, T divisor)
30 : : {
31 : 7 : 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 : 86 : round(T num, T den, T rem, RT2T<RoundType::never>)
68 : : {
69 : 86 : if (rem == 0)
70 : 0 : return num;
71 : 86 : throw std::domain_error("Rounding required when 'never round' specified.");
72 : : }
73 : :
74 : : template <typename T> inline T
75 : 1 : 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 : 1 : if (rem == 0)
80 : 0 : return num;
81 : : // floor num==0 that is the quotient of two numbers with opposite signs
82 : 1 : if (num < 0 || (num == 0 && !quotient_is_positive(rem, den)))
83 : 0 : return num - 1;
84 : 1 : 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 : 5109 : round(T num, T den, T rem, RT2T<RoundType::truncate>)
124 : : {
125 : 5109 : 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 : 1353 : round(T num, T den, T rem, RT2T<RoundType::half_up>)
174 : : {
175 : 1353 : if (rem == 0)
176 : 0 : return num;
177 : 1353 : if (std::abs(rem) * 2 >= std::abs(den))
178 : : {
179 : 681 : if (num == 0)
180 : 7 : return (!quotient_is_positive(rem, den) ? -1 : 1);
181 : 674 : return num + (num < 0 ? -1 : 1);
182 : : }
183 : 672 : return num;
184 : : }
185 : :
186 : : template <> inline GncInt128
187 : 53 : round<GncInt128>(GncInt128 num, GncInt128 den, GncInt128 rem,
188 : : RT2T<RoundType::half_up>)
189 : : {
190 : 53 : if (rem == 0)
191 : 0 : return num;
192 : 53 : if (rem.abs() * 2 >= den.abs())
193 : 23 : return num + (num.isNeg() ? -1 : 1);
194 : 30 : return num;
195 : : }
196 : :
197 : : template <typename T> inline T
198 : 293 : round(T num, T den, T rem, RT2T<RoundType::bankers>)
199 : : {
200 : 293 : if (rem == 0)
201 : 0 : return num;
202 : 426 : if (std::abs(rem * 2) > std::abs(den) ||
203 : 133 : (std::abs(rem * 2) == std::abs(den) && num % 2))
204 : : {
205 : 188 : if (num == 0)
206 : 0 : return (!quotient_is_positive(rem, den) ? -1 : 1);
207 : 188 : return num + (num < 0 ? -1 : 1);
208 : : }
209 : 105 : 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__
|