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 : : #include <sstream>
24 : : #include <cstdint>
25 : : #include "gnc-rational.hpp"
26 : : #include "gnc-numeric.hpp"
27 : :
28 : :
29 : 515742 : GncRational::GncRational(GncNumeric n) noexcept :
30 : 515742 : m_num(n.num()), m_den(n.denom())
31 : : {
32 : 515742 : if (m_den.isNeg())
33 : : {
34 : 0 : m_num *= -m_den;
35 : 0 : m_den = 1;
36 : : }
37 : 515742 : }
38 : :
39 : 23443 : GncRational::GncRational (gnc_numeric n) noexcept :
40 : 23443 : m_num (n.num), m_den (n.denom)
41 : : {
42 : 23443 : if (m_den.isNeg())
43 : : {
44 : 0 : m_num *= -m_den;
45 : 0 : m_den = 1;
46 : : }
47 : 23443 : }
48 : :
49 : : bool
50 : 544904 : GncRational::valid() const noexcept
51 : : {
52 : 544904 : if (m_num.valid() && m_den.valid())
53 : 544904 : return true;
54 : 0 : return false;
55 : : }
56 : :
57 : : bool
58 : 5062 : GncRational::is_big() const noexcept
59 : : {
60 : 5062 : if (m_num.isBig() || m_den.isBig())
61 : 33 : return true;
62 : 5029 : return false;
63 : : }
64 : :
65 : 11938 : GncRational::operator gnc_numeric () const noexcept
66 : : {
67 : 11938 : if (!valid())
68 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
69 : : try
70 : : {
71 : 11938 : 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 : 2670 : GncRational::cmp(GncRational b)
133 : : {
134 : 2670 : 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 : 2670 : auto gcd = m_den.gcd(b.denom());
140 : 2670 : GncInt128 a_num(m_num * b.denom() / gcd), b_num(b.num() * m_den / gcd);
141 : 2670 : return a_num < b_num ? -1 : b_num < a_num ? 1 : 0;
142 : : }
143 : :
144 : : GncRational::round_param
145 : 6141 : GncRational::prepare_conversion (GncInt128 new_denom) const
146 : : {
147 : 6141 : if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
148 : 518 : return {m_num, m_den, 0};
149 : 5623 : GncRational conversion(new_denom, m_den);
150 : 5623 : auto red_conv = conversion.reduce();
151 : 5623 : GncInt128 old_num(m_num);
152 : 5623 : auto new_num = old_num * red_conv.num();
153 : 5623 : if (new_num.isOverflow())
154 : 0 : throw std::overflow_error("Conversion overflow");
155 : 5623 : auto rem = new_num % red_conv.denom();
156 : 5623 : new_num /= red_conv.denom();
157 : 5623 : 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 : : return not_frac ?
176 : 0 : powten(digits < figs ? figs - digits - 1 : 0) :
177 : 0 : powten(figs + digits);
178 : : }
179 : :
180 : : GncRational
181 : 101611 : GncRational::reduce() const
182 : : {
183 : 101611 : auto gcd = m_den.gcd(m_num);
184 : 101611 : if (gcd.isNan() || gcd.isOverflow())
185 : 0 : throw std::overflow_error("Reduce failed, calculation of gcd overflowed.");
186 : 203222 : return GncRational(m_num / gcd, m_den / gcd);
187 : : }
188 : :
189 : : GncRational
190 : 6564 : GncRational::round_to_numeric() const
191 : : {
192 : 6564 : unsigned int ll_bits = GncInt128::legbits;
193 : 6564 : if (m_num.isZero())
194 : 3250 : return GncRational(); //Default constructor makes 0/1
195 : 3314 : if (!(m_num.isBig() || m_den.isBig()))
196 : 3258 : 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 : 257107 : operator+(GncRational a, GncRational b)
259 : : {
260 : 257107 : if (!(a.valid() && b.valid()))
261 : 0 : throw std::range_error("Operator+ called with out-of-range operand.");
262 : 257107 : GncInt128 lcm = a.denom().lcm(b.denom());
263 : 257107 : GncInt128 num(a.num() * lcm / a.denom() + b.num() * lcm / b.denom());
264 : 257107 : if (!(lcm.valid() && num.valid()))
265 : 0 : throw std::overflow_error("Operator+ overflowed.");
266 : 257107 : GncRational retval(num, lcm);
267 : 514214 : 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 : 5104 : operator*(GncRational a, GncRational b)
279 : : {
280 : 5104 : if (!(a.valid() && b.valid()))
281 : 0 : throw std::range_error("Operator* called with out-of-range operand.");
282 : 5104 : GncInt128 num (a.num() * b.num()), den(a.denom() * b.denom());
283 : 5104 : if (!(num.valid() && den.valid()))
284 : 0 : throw std::overflow_error("Operator* overflowed.");
285 : 5104 : GncRational retval(num, den);
286 : 10208 : return retval;
287 : : }
288 : :
289 : : GncRational
290 : 1770 : operator/(GncRational a, GncRational b)
291 : : {
292 : 1770 : if (!(a.valid() && b.valid()))
293 : 0 : throw std::range_error("Operator/ called with out-of-range operand.");
294 : 1770 : auto a_num = a.num(), b_num = b.num(), a_den = a.denom(), b_den = b.denom();
295 : 1770 : if (b_num == 0)
296 : 1 : throw std::underflow_error("Divide by 0.");
297 : 1769 : if (b_num.isNeg())
298 : : {
299 : 142 : a_num = -a_num;
300 : 142 : 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 : 1769 : if (a_den == b_den)
307 : 1369 : return GncRational(a_num, b_num);
308 : :
309 : : /* Protect against possibly preventable overflow: */
310 : 1200 : if (a_num.isBig() || a_den.isBig() ||
311 : 1200 : 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 : 400 : GncInt128 num(a_num * b_den), den(a_den * b_num);
319 : 400 : if (!(num.valid() && den.valid()))
320 : 0 : throw std::overflow_error("Operator/ overflowed.");
321 : 400 : return GncRational(num, den);
322 : : }
|