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 : #include <sstream>
24 : #include <cstdint>
25 : #include "gnc-rational.hpp"
26 : #include "gnc-numeric.hpp"
27 :
28 :
29 394068 : GncRational::GncRational(GncNumeric n) noexcept :
30 394068 : m_num(n.num()), m_den(n.denom())
31 : {
32 394068 : if (m_den.isNeg())
33 : {
34 0 : m_num *= -m_den;
35 0 : m_den = 1;
36 : }
37 394068 : }
38 :
39 19462 : GncRational::GncRational (gnc_numeric n) noexcept :
40 19462 : m_num (n.num), m_den (n.denom)
41 : {
42 19462 : if (m_den.isNeg())
43 : {
44 0 : m_num *= -m_den;
45 0 : m_den = 1;
46 : }
47 19462 : }
48 :
49 : bool
50 417197 : GncRational::valid() const noexcept
51 : {
52 417197 : if (m_num.valid() && m_den.valid())
53 417197 : return true;
54 0 : return false;
55 : }
56 :
57 : bool
58 2800 : GncRational::is_big() const noexcept
59 : {
60 2800 : if (m_num.isBig() || m_den.isBig())
61 33 : return true;
62 2767 : return false;
63 : }
64 :
65 9977 : GncRational::operator gnc_numeric () const noexcept
66 : {
67 9977 : if (!valid())
68 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
69 : try
70 : {
71 9977 : return {static_cast<int64_t>(m_num), static_cast<int64_t>(m_den)};
72 : }
73 0 : catch (std::overflow_error&)
74 : {
75 0 : return gnc_numeric_error (GNC_ERROR_OVERFLOW);
76 0 : }
77 : }
78 :
79 : GncRational
80 7 : GncRational::operator-() const noexcept
81 : {
82 7 : return GncRational(-m_num, m_den);
83 : }
84 :
85 : GncRational
86 0 : GncRational::inv () const noexcept
87 : {
88 0 : if (m_num == 0)
89 0 : return *this;
90 0 : if (m_num < 0)
91 0 : return GncRational(-m_den, -m_num);
92 0 : return GncRational(m_den, m_num);
93 : }
94 :
95 : GncRational
96 0 : GncRational::abs() const noexcept
97 : {
98 0 : if (m_num < 0)
99 0 : return -*this;
100 0 : return *this;
101 : }
102 :
103 : void
104 0 : GncRational::operator+=(GncRational b)
105 : {
106 0 : GncRational new_val = *this + b;
107 0 : *this = std::move(new_val);
108 0 : }
109 :
110 : void
111 0 : GncRational::operator-=(GncRational b)
112 : {
113 0 : GncRational new_val = *this - b;
114 0 : *this = std::move(new_val);
115 0 : }
116 :
117 : void
118 0 : GncRational::operator*=(GncRational b)
119 : {
120 0 : GncRational new_val = *this * b;
121 0 : *this = std::move(new_val);
122 0 : }
123 :
124 : void
125 0 : GncRational::operator/=(GncRational b)
126 : {
127 0 : GncRational new_val = *this / b;
128 0 : *this = std::move(new_val);
129 0 : }
130 :
131 : int
132 2612 : GncRational::cmp(GncRational b)
133 : {
134 2612 : if (m_den == b.denom())
135 : {
136 0 : auto b_num = b.num();
137 0 : return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
138 : }
139 2612 : auto gcd = m_den.gcd(b.denom());
140 2612 : GncInt128 a_num(m_num * b.denom() / gcd), b_num(b.num() * m_den / gcd);
141 2612 : return a_num < b_num ? -1 : b_num < a_num ? 1 : 0;
142 : }
143 :
144 : GncRational::round_param
145 3477 : GncRational::prepare_conversion (GncInt128 new_denom) const
146 : {
147 3477 : if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
148 574 : return {m_num, m_den, 0};
149 2903 : GncRational conversion(new_denom, m_den);
150 2903 : auto red_conv = conversion.reduce();
151 2903 : GncInt128 old_num(m_num);
152 2903 : auto new_num = old_num * red_conv.num();
153 2903 : if (new_num.isOverflow())
154 0 : throw std::overflow_error("Conversion overflow");
155 2903 : auto rem = new_num % red_conv.denom();
156 2903 : new_num /= red_conv.denom();
157 2903 : return {new_num, red_conv.denom(), rem};
158 : }
159 :
160 : GncInt128
161 0 : GncRational::sigfigs_denom(unsigned figs) const noexcept
162 : {
163 0 : if (m_num == 0)
164 0 : return 1;
165 :
166 0 : auto num_abs = m_num.abs();
167 0 : bool not_frac = num_abs > m_den;
168 0 : int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
169 0 : unsigned digits{};
170 0 : while (val >= 10)
171 : {
172 0 : ++digits;
173 0 : val /= 10;
174 : }
175 0 : return not_frac ?
176 0 : powten(digits < figs ? figs - digits - 1 : 0) :
177 0 : powten(figs + digits);
178 : }
179 :
180 : GncRational
181 34970 : GncRational::reduce() const
182 : {
183 34970 : auto gcd = m_den.gcd(m_num);
184 34970 : if (gcd.isNan() || gcd.isOverflow())
185 0 : throw std::overflow_error("Reduce failed, calculation of gcd overflowed.");
186 69940 : return GncRational(m_num / gcd, m_den / gcd);
187 : }
188 :
189 : GncRational
190 6806 : GncRational::round_to_numeric() const
191 : {
192 6806 : unsigned int ll_bits = GncInt128::legbits;
193 6806 : if (m_num.isZero())
194 3360 : return GncRational(); //Default constructor makes 0/1
195 3446 : if (!(m_num.isBig() || m_den.isBig()))
196 3390 : return *this;
197 56 : if (m_num.abs() > m_den)
198 : {
199 51 : auto quot(m_num / m_den);
200 51 : if (quot.isBig())
201 : {
202 28 : std::ostringstream msg;
203 : msg << " Cannot be represented as a "
204 28 : << "GncNumeric. Its integer value is too large.\n";
205 28 : throw std::overflow_error(msg.str());
206 28 : }
207 23 : GncRational new_v;
208 75 : while (new_v.num().isZero())
209 : {
210 : try
211 : {
212 52 : new_v = convert<RoundType::half_down>(m_den / (m_num.abs() >> ll_bits));
213 52 : if (new_v.is_big())
214 : {
215 29 : --ll_bits;
216 29 : new_v = GncRational();
217 : }
218 : }
219 0 : catch(const std::overflow_error& err)
220 : {
221 0 : --ll_bits;
222 0 : }
223 : }
224 23 : return new_v;
225 : }
226 5 : auto quot(m_den / m_num);
227 5 : if (quot.isBig())
228 0 : return GncRational(); //Smaller than can be represented as a GncNumeric
229 5 : GncRational new_v;
230 17 : while (new_v.num().isZero())
231 : {
232 15 : auto divisor = m_den >> ll_bits;
233 15 : if (m_num.isBig())
234 : {
235 9 : GncInt128 oldnum(m_num), num, rem;
236 9 : oldnum.div(divisor, num, rem);
237 9 : auto den = m_den / divisor;
238 9 : num += rem * 2 >= den ? 1 : 0;
239 9 : if (num.isBig() || den.isBig())
240 : {
241 6 : --ll_bits;
242 6 : continue;
243 : }
244 3 : GncRational new_rational(num, den);
245 3 : return new_rational;
246 : }
247 6 : new_v = convert<RoundType::half_down>(m_den / divisor);
248 6 : if (new_v.is_big())
249 : {
250 4 : --ll_bits;
251 4 : new_v = GncRational();
252 : }
253 : }
254 2 : return new_v;
255 : }
256 :
257 : GncRational
258 197247 : operator+(GncRational a, GncRational b)
259 : {
260 197247 : if (!(a.valid() && b.valid()))
261 0 : throw std::range_error("Operator+ called with out-of-range operand.");
262 197247 : GncInt128 lcm = a.denom().lcm(b.denom());
263 197247 : GncInt128 num(a.num() * lcm / a.denom() + b.num() * lcm / b.denom());
264 197247 : if (!(lcm.valid() && num.valid()))
265 0 : throw std::overflow_error("Operator+ overflowed.");
266 197247 : GncRational retval(num, lcm);
267 394494 : return retval;
268 : }
269 :
270 : GncRational
271 7 : operator-(GncRational a, GncRational b)
272 : {
273 7 : GncRational retval = a + (-b);
274 7 : return retval;
275 : }
276 :
277 : GncRational
278 3171 : operator*(GncRational a, GncRational b)
279 : {
280 3171 : if (!(a.valid() && b.valid()))
281 0 : throw std::range_error("Operator* called with out-of-range operand.");
282 3171 : GncInt128 num (a.num() * b.num()), den(a.denom() * b.denom());
283 3171 : if (!(num.valid() && den.valid()))
284 0 : throw std::overflow_error("Operator* overflowed.");
285 3171 : GncRational retval(num, den);
286 6342 : return retval;
287 : }
288 :
289 : GncRational
290 1821 : operator/(GncRational a, GncRational b)
291 : {
292 1821 : if (!(a.valid() && b.valid()))
293 0 : throw std::range_error("Operator/ called with out-of-range operand.");
294 1821 : auto a_num = a.num(), b_num = b.num(), a_den = a.denom(), b_den = b.denom();
295 1821 : if (b_num == 0)
296 1 : throw std::underflow_error("Divide by 0.");
297 1820 : if (b_num.isNeg())
298 : {
299 100 : a_num = -a_num;
300 100 : b_num = -b_num;
301 : }
302 :
303 : /* q = (a_num * b_den)/(b_num * a_den). If a_den == b_den they cancel out
304 : * and it's just a_num/b_num.
305 : */
306 1820 : if (a_den == b_den)
307 1382 : return GncRational(a_num, b_num);
308 :
309 : /* Protect against possibly preventable overflow: */
310 1314 : if (a_num.isBig() || a_den.isBig() ||
311 1314 : b_num.isBig() || b_den.isBig())
312 : {
313 0 : GncInt128 gcd = b_den.gcd(a_den);
314 0 : b_den /= gcd;
315 0 : a_den /= gcd;
316 : }
317 :
318 438 : GncInt128 num(a_num * b_den), den(a_den * b_num);
319 438 : if (!(num.valid() && den.valid()))
320 0 : throw std::overflow_error("Operator/ overflowed.");
321 438 : return GncRational(num, den);
322 : }
|