Branch data Line data Source code
1 : : /********************************************************************
2 : : * gnc-numeric.c -- an exact-number library for accounting use *
3 : : * Copyright (C) 2000 Bill Gribble *
4 : : * Copyright (C) 2004 Linas Vepstas <linas@linas.org> *
5 : : * *
6 : : * This program is free software; you can redistribute it and/or *
7 : : * modify it under the terms of the GNU General Public License as *
8 : : * published by the Free Software Foundation; either version 2 of *
9 : : * the License, or (at your option) any later version. *
10 : : * *
11 : : * This program is distributed in the hope that it will be useful, *
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 : : * GNU General Public License for more details. *
15 : : * *
16 : : * You should have received a copy of the GNU General Public License*
17 : : * along with this program; if not, contact: *
18 : : * *
19 : : * Free Software Foundation Voice: +1-617-542-5942 *
20 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
22 : : * *
23 : : *******************************************************************/
24 : :
25 : : #include <glib.h>
26 : :
27 : : #include <cmath>
28 : : #include <cstdio>
29 : : #include <cstdlib>
30 : : #include <cstring>
31 : : #include <cstdint>
32 : : #include <sstream>
33 : : #include <boost/regex.hpp>
34 : : #include <boost/locale/encoding_utf.hpp>
35 : :
36 : : #include <config.h>
37 : : #include <stdexcept>
38 : : #include <stdint.h>
39 : : #include "gnc-int128.hpp"
40 : : #include "qof.h"
41 : :
42 : : #include "gnc-numeric.hpp"
43 : : #include "gnc-rational.hpp"
44 : :
45 : : #include <optional>
46 : : #include <charconv>
47 : :
48 : : static QofLogModule log_module = "qof";
49 : :
50 : : static const uint8_t max_leg_digits{18};
51 : : static const int64_t pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
52 : : 10000000, 100000000, 1000000000,
53 : : INT64_C(10000000000), INT64_C(100000000000),
54 : : INT64_C(1000000000000), INT64_C(10000000000000),
55 : : INT64_C(100000000000000),
56 : : INT64_C(1000000000000000),
57 : : INT64_C(10000000000000000),
58 : : INT64_C(100000000000000000),
59 : : INT64_C(1000000000000000000)};
60 : : #define POWTEN_OVERFLOW -5
61 : :
62 : : int64_t
63 : 51315 : powten (unsigned int exp)
64 : : {
65 : 51315 : if (exp > max_leg_digits)
66 : 1034 : exp = max_leg_digits;
67 : 51315 : return pten[exp];
68 : : }
69 : :
70 : 256875 : GncNumeric::GncNumeric(GncRational rr)
71 : : {
72 : : /* Can't use isValid here because we want to throw different exceptions. */
73 : 256875 : if (rr.num().isNan() || rr.denom().isNan())
74 : 0 : throw std::underflow_error("Operation resulted in NaN.");
75 : 256875 : if (rr.num().isOverflow() || rr.denom().isOverflow())
76 : 0 : throw std::overflow_error("Operation overflowed a 128-bit int.");
77 : 256875 : if (rr.num().isBig() || rr.denom().isBig())
78 : : {
79 : 66 : GncRational reduced(rr.reduce());
80 : 66 : rr = reduced.round_to_numeric(); // A no-op if it's already small.
81 : : }
82 : 256847 : m_num = static_cast<int64_t>(rr.num());
83 : 256847 : m_den = static_cast<int64_t>(rr.denom());
84 : 256847 : }
85 : :
86 : 357 : GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
87 : : {
88 : : static uint64_t max_leg_value{INT64_C(1000000000000000000)};
89 : 357 : if (std::isnan(d) || fabs(d) > max_leg_value)
90 : : {
91 : 0 : std::ostringstream msg;
92 : 0 : msg << "Unable to construct a GncNumeric from " << d << ".\n";
93 : 0 : throw std::invalid_argument(msg.str());
94 : 0 : }
95 : 357 : constexpr auto max_num = static_cast<double>(INT64_MAX);
96 : 357 : auto logval = log10(fabs(d));
97 : : int64_t den;
98 : : uint8_t den_digits;
99 : 357 : if (logval > 0.0)
100 : 328 : den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval));
101 : : else
102 : 29 : den_digits = max_leg_digits;
103 : 357 : den = powten(den_digits);
104 : 357 : auto num_d = static_cast<double>(den) * d;
105 : 629 : while (fabs(num_d) > max_num && den_digits > 1)
106 : : {
107 : 272 : den = powten(--den_digits);
108 : 272 : num_d = static_cast<double>(den) * d;
109 : : }
110 : 357 : auto num = static_cast<int64_t>(floor(num_d));
111 : :
112 : 357 : if (num == 0)
113 : 6 : return;
114 : 351 : GncNumeric q(num, den);
115 : 351 : auto r = q.reduce();
116 : 351 : m_num = r.num();
117 : 351 : m_den = r.denom();
118 : : }
119 : :
120 : : using boost::regex;
121 : : using boost::regex_search;
122 : : using boost::smatch;
123 : :
124 : :
125 : : static std::pair<int64_t, int64_t>
126 : 14 : reduce_number_pair(std::pair<GncInt128, GncInt128>num_pair,
127 : : const std::string& num_str, bool autoround)
128 : : {
129 : 14 : auto [n, d] = num_pair;
130 : 14 : if (!autoround && n.isBig()) {
131 : 0 : std::ostringstream errmsg;
132 : : errmsg << "Decimal string " << num_str
133 : 0 : << "can't be represented in a GncNumeric without rounding.";
134 : 0 : throw std::overflow_error(errmsg.str());
135 : 0 : }
136 : 14 : while (n.isBig() && d > 0) {
137 : 0 : n >>= 1;
138 : 0 : d >>= 1;
139 : : }
140 : 14 : if (n.isBig()) // Shouldn't happen, of course
141 : : {
142 : 0 : std::ostringstream errmsg;
143 : : errmsg << "Decimal string " << num_str
144 : : << " can't be represented in a GncNumeric, even after reducing "
145 : 0 : "denom to "
146 : 0 : << d;
147 : 0 : throw std::overflow_error(errmsg.str());
148 : 0 : }
149 : 14 : return std::make_pair(static_cast<int64_t>(n), static_cast<int64_t>(d));
150 : : }
151 : :
152 : : static std::pair<GncInt128, int64_t>
153 : 14 : numeric_from_decimal_match(const std::string& integer, const std::string& decimal)
154 : : {
155 : 14 : auto neg = (!integer.empty() && integer[0] == '-');
156 : 14 : GncInt128 high((neg && integer.length() > 1) || (!neg && !integer.empty())
157 : 13 : ? stoll(integer)
158 : 28 : : 0);
159 : 14 : GncInt128 low{ decimal.empty() ? 0 : stoll(decimal)};
160 : 14 : auto exp10 = decimal.length();
161 : 14 : int64_t d = powten(exp10);
162 : 14 : GncInt128 n = high * d + (neg ? -low : low);
163 : 14 : while (exp10 > max_leg_digits) {
164 : : /* If the arg to powten is bigger than max_leg_digits
165 : : it returns 10**max_leg_digits so reduce exp10 by
166 : : that amount */
167 : 0 : n = n / powten(exp10 - max_leg_digits);
168 : 0 : exp10 -= max_leg_digits;
169 : : }
170 : 28 : return std::make_pair(n, d);
171 : : }
172 : :
173 : : static std::pair<GncInt128, GncInt128>
174 : 0 : numeric_from_scientific_match(smatch &m)
175 : : {
176 : 0 : int exp{m[4].matched ? stoi(m[4].str()) : 0};
177 : 0 : auto neg_exp{exp < 0};
178 : 0 : exp = neg_exp ? -exp : exp;
179 : 0 : if (exp >= max_leg_digits)
180 : : {
181 : 0 : std::ostringstream errmsg;
182 : 0 : errmsg << "Exponent " << m[3].str() << " in match " << m[0].str()
183 : 0 : << " exceeds range that GnuCash can parse.";
184 : 0 : throw std::overflow_error(errmsg.str());
185 : 0 : }
186 : :
187 : 0 : GncInt128 num, denom;
188 : 0 : auto mult = powten(exp);
189 : :
190 : 0 : if (m[1].matched)
191 : : {
192 : 0 : denom = neg_exp ? mult : 1;
193 : 0 : num = neg_exp ? stoll(m[1].str()) : mult * stoll(m[1].str());
194 : : }
195 : : else
196 : : {
197 : 0 : auto [d_num, d_denom] = numeric_from_decimal_match(m[2].str(), m[3].str());
198 : :
199 : 0 : if (neg_exp || d_denom > mult)
200 : : {
201 : 0 : num = d_num;
202 : 0 : denom = neg_exp ? d_denom * mult : d_denom / mult;
203 : : }
204 : : else
205 : : {
206 : 0 : num = d_num * mult / d_denom;
207 : 0 : denom = 1;
208 : : }
209 : : }
210 : 0 : return std::make_pair(num, denom);
211 : : }
212 : :
213 : : static std::optional<gnc_numeric>
214 : 5162 : fast_numeral_rational (const char* str)
215 : : {
216 : 5162 : if (!str || !str[0])
217 : 1 : return {};
218 : :
219 : : // because minint64 = -9223372036854775808 and has 20 characters,
220 : : // the maximum strlen to handle is 20+19+1 = 40. 48 is a nice
221 : : // number in 64-bit.
222 : 5161 : auto end_ptr{(const char*)memchr (str, '\0', 48)};
223 : 5161 : if (!end_ptr)
224 : 0 : return {};
225 : :
226 : 5161 : int64_t num, denom{};
227 : 5161 : auto result = std::from_chars (str, end_ptr, num);
228 : 5161 : if (result.ec != std::errc())
229 : 6 : return {};
230 : :
231 : 5155 : if (result.ptr == end_ptr)
232 : 1 : return gnc_numeric_create (num, 1);
233 : :
234 : 5154 : if (*result.ptr != '/')
235 : 20 : return {};
236 : :
237 : 5134 : result = std::from_chars (result.ptr + 1, end_ptr, denom);
238 : 5134 : if (result.ec != std::errc() || result.ptr != end_ptr || denom <= 0)
239 : 0 : return {};
240 : :
241 : 5134 : return gnc_numeric_create (num, denom);
242 : : }
243 : :
244 : 19 : GncNumeric::GncNumeric(const std::string &str, bool autoround) {
245 : 27 : static const std::string maybe_sign ("(-?)");
246 : 27 : static const std::string opt_signed_int("(-?[0-9]*)");
247 : 27 : static const std::string unsigned_int("([0-9]+)");
248 : 27 : static const std::string hex_frag("(0[xX][A-Fa-f0-9]+)");
249 : 27 : static const std::string slash("[ \\t]*/[ \\t]*");
250 : 27 : static const std::string whitespace("[ \\t]+");
251 : : /* The llvm standard C++ library refused to recognize the - in the
252 : : * opt_signed_int pattern with the default ECMAScript syntax so we use the
253 : : * awk syntax.
254 : : */
255 : 19 : static const regex numeral(opt_signed_int);
256 : 19 : static const regex hex(hex_frag);
257 : 19 : static const regex numeral_rational(opt_signed_int + slash + unsigned_int);
258 : 19 : static const regex integer_and_fraction(maybe_sign + unsigned_int + whitespace + unsigned_int + slash + unsigned_int);
259 : 19 : static const regex hex_rational(hex_frag + slash + hex_frag);
260 : 19 : static const regex hex_over_num(hex_frag + slash + unsigned_int);
261 : 19 : static const regex num_over_hex(opt_signed_int + slash + hex_frag);
262 : 19 : static const regex decimal(opt_signed_int + "[.,]" + unsigned_int);
263 : 19 : static const regex scientific("(?:(-?[0-9]+[.,]?)|(-?[0-9]*)[.,]([0-9]+))[Ee](-?[0-9]+)");
264 : 19 : static const regex has_hex_prefix(".*0[xX]$");
265 : 57 : smatch m, x;
266 : : /* The order of testing the regexes is from the more restrictve to the less
267 : : * restrictive, as less-restrictive ones will match patterns that would also
268 : : * match the more-restrictive and so invoke the wrong construction.
269 : : */
270 : 19 : if (str.empty())
271 : : throw std::invalid_argument(
272 : 1 : "Can't construct a GncNumeric from an empty string.");
273 : 18 : if (auto res = fast_numeral_rational (str.c_str()))
274 : : {
275 : 0 : m_num = res->num;
276 : 0 : m_den = res->denom;
277 : 0 : return;
278 : : }
279 : 18 : if (regex_search(str, m, hex_rational))
280 : : {
281 : 0 : GncNumeric n(stoll(m[1].str(), nullptr, 16),
282 : 0 : stoll(m[2].str(), nullptr, 16));
283 : :
284 : 0 : m_num = n.num();
285 : 0 : m_den = n.denom();
286 : 0 : return;
287 : : }
288 : 18 : if (regex_search(str, m, hex_over_num))
289 : : {
290 : 0 : GncNumeric n(stoll(m[1].str(), nullptr, 16), stoll(m[2].str()));
291 : 0 : m_num = n.num();
292 : 0 : m_den = n.denom();
293 : 0 : return;
294 : : }
295 : 18 : if (regex_search(str, m, num_over_hex))
296 : : {
297 : 0 : GncNumeric n(stoll(m[1].str()), stoll(m[2].str(), nullptr, 16));
298 : 0 : m_num = n.num();
299 : 0 : m_den = n.denom();
300 : 0 : return;
301 : : }
302 : 18 : if (regex_search(str, m, integer_and_fraction))
303 : : {
304 : 2 : GncNumeric n(stoll(m[3].str()), stoll(m[4].str()));
305 : 2 : n += stoll(m[2].str());
306 : 2 : m_num = m[1].str().empty() ? n.num() : -n.num();
307 : 2 : m_den = n.denom();
308 : 2 : return;
309 : : }
310 : 16 : if (regex_search(str, m, numeral_rational))
311 : : {
312 : 1 : GncNumeric n(stoll(m[1].str()), stoll(m[2].str()));
313 : 1 : m_num = n.num();
314 : 1 : m_den = n.denom();
315 : 1 : return;
316 : : }
317 : 15 : if (regex_search(str, m, scientific) && ! regex_match(m.prefix().str(), x, has_hex_prefix))
318 : : {
319 : 0 : auto [num, denom] =
320 : 0 : reduce_number_pair(numeric_from_scientific_match(m),
321 : : str, autoround);
322 : 0 : m_num = num;
323 : 0 : m_den = denom;
324 : 0 : return;
325 : : }
326 : 15 : if (regex_search(str, m, decimal))
327 : : {
328 : 14 : std::string integer{m[1].matched ? m[1].str() : ""};
329 : 14 : std::string decimal{m[2].matched ? m[2].str() : ""};
330 : 14 : auto [num, denom] =
331 : 14 : reduce_number_pair(numeric_from_decimal_match(integer, decimal),
332 : : str, autoround);
333 : 14 : m_num = num;
334 : 14 : m_den = denom;
335 : 14 : return;
336 : 14 : }
337 : 1 : if (regex_search(str, m, hex))
338 : : {
339 : 0 : GncNumeric n(stoll(m[1].str(), nullptr, 16), INT64_C(1));
340 : 0 : m_num = n.num();
341 : 0 : m_den = n.denom();
342 : 0 : return;
343 : : }
344 : 1 : if (regex_search(str, m, numeral))
345 : : {
346 : 2 : GncNumeric n(stoll(m[1].str()), INT64_C(1));
347 : 0 : m_num = n.num();
348 : 0 : m_den = n.denom();
349 : 0 : return;
350 : : }
351 : 0 : std::ostringstream errmsg;
352 : 0 : errmsg << "String " << str << " contains no recognizable numeric value.";
353 : 0 : throw std::invalid_argument(errmsg.str());
354 : 21 : }
355 : :
356 : 539203 : GncNumeric::operator gnc_numeric() const noexcept
357 : : {
358 : 539203 : return {m_num, m_den};
359 : : }
360 : :
361 : 0 : GncNumeric::operator double() const noexcept
362 : : {
363 : 0 : return static_cast<double>(m_num) / static_cast<double>(m_den);
364 : : }
365 : :
366 : : GncNumeric
367 : 70542 : GncNumeric::operator-() const noexcept
368 : : {
369 : 70542 : GncNumeric b(*this);
370 : 70542 : b.m_num = - b.m_num;
371 : 70542 : return b;
372 : : }
373 : :
374 : : GncNumeric
375 : 708 : GncNumeric::inv() const noexcept
376 : : {
377 : 708 : if (m_num == 0)
378 : 0 : return *this;
379 : 708 : if (m_num < 0)
380 : 0 : return GncNumeric(-m_den, -m_num);
381 : 708 : return GncNumeric(m_den, m_num);
382 : : }
383 : :
384 : : GncNumeric
385 : 0 : GncNumeric::abs() const noexcept
386 : : {
387 : 0 : if (m_num < 0)
388 : 0 : return -*this;
389 : 0 : return *this;
390 : : }
391 : :
392 : : GncNumeric
393 : 4382 : GncNumeric::reduce() const noexcept
394 : : {
395 : 4382 : return static_cast<GncNumeric>(GncRational(*this).reduce());
396 : : }
397 : :
398 : : GncNumeric::round_param
399 : 503122 : GncNumeric::prepare_conversion(int64_t new_denom) const
400 : : {
401 : 503122 : if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
402 : 411582 : return {m_num, m_den, 0};
403 : 91540 : GncRational conversion(new_denom, m_den);
404 : 91540 : auto red_conv = conversion.reduce();
405 : 91540 : GncInt128 old_num(m_num);
406 : 91540 : auto new_num = old_num * red_conv.num();
407 : 91540 : auto rem = new_num % red_conv.denom();
408 : 91540 : new_num /= red_conv.denom();
409 : 91540 : if (new_num.isBig())
410 : : {
411 : 15 : GncRational rr(new_num, new_denom);
412 : 15 : GncNumeric nn(rr);
413 : 15 : rr = rr.convert<RoundType::truncate>(new_denom);
414 : 15 : return {static_cast<int64_t>(rr.num()), new_denom, 0};
415 : : }
416 : 91525 : return {static_cast<int64_t>(new_num),
417 : 91525 : static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
418 : : }
419 : :
420 : : int64_t
421 : 33 : GncNumeric::sigfigs_denom(unsigned figs) const noexcept
422 : : {
423 : 33 : if (m_num == 0)
424 : 4 : return 1;
425 : :
426 : 29 : int64_t num_abs{std::abs(m_num)};
427 : 29 : bool not_frac = num_abs > m_den;
428 : 29 : int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
429 : 29 : unsigned digits{};
430 : 44 : while (val >= 10)
431 : : {
432 : 15 : ++digits;
433 : 15 : val /= 10;
434 : : }
435 : 29 : return not_frac ?
436 : 23 : powten(digits < figs ? figs - digits - 1 : 0) :
437 : 29 : powten(figs + digits);
438 : : }
439 : :
440 : : std::string
441 : 0 : GncNumeric::to_string() const noexcept
442 : : {
443 : 0 : std::ostringstream out;
444 : 0 : out << *this;
445 : 0 : return out.str();
446 : 0 : }
447 : :
448 : : bool
449 : 32341 : GncNumeric::is_decimal() const noexcept
450 : : {
451 : 65837 : for (unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
452 : : {
453 : 65065 : if (m_den == pten[pwr])
454 : 31277 : return true;
455 : 33788 : if (m_den % pten[pwr])
456 : 292 : return false;
457 : : }
458 : 772 : return false;
459 : : }
460 : :
461 : : GncNumeric
462 : 32061 : GncNumeric::to_decimal(unsigned int max_places) const
463 : : {
464 : 32061 : if (max_places > max_leg_digits)
465 : 0 : max_places = max_leg_digits;
466 : :
467 : 32061 : if (m_num == 0)
468 : 0 : return GncNumeric();
469 : :
470 : 32061 : if (is_decimal())
471 : : {
472 : 30997 : if (m_num == 0 || m_den < powten(max_places))
473 : 30997 : return *this; // Nothing to do.
474 : : /* See if we can reduce m_num to fit in max_places */
475 : 0 : auto excess = m_den / powten(max_places);
476 : 0 : if (m_num % excess)
477 : : {
478 : 0 : std::ostringstream msg;
479 : 0 : msg << "GncNumeric " << *this
480 : 0 : << " could not be represented in " << max_places
481 : 0 : << " decimal places without rounding.\n";
482 : 0 : throw std::range_error(msg.str());
483 : 0 : }
484 : 0 : return GncNumeric(m_num / excess, powten(max_places));
485 : : }
486 : 1064 : GncRational rr(*this);
487 : 1064 : rr = rr.convert<RoundType::never>(powten(max_places)); //May throw
488 : : /* rr might have gotten reduced a bit too much; if so, put it back: */
489 : 978 : unsigned int pwr{1};
490 : 18582 : for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
491 : 978 : auto reduce_to = powten(pwr);
492 : 978 : GncInt128 rr_num(rr.num()), rr_den(rr.denom());
493 : 978 : if (rr_den % reduce_to)
494 : : {
495 : 0 : auto factor(reduce_to / rr.denom());
496 : 0 : rr_num *= factor;
497 : 0 : rr_den *= factor;
498 : : }
499 : 16819 : while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
500 : : {
501 : 15841 : rr_num /= 10;
502 : 15841 : rr_den /= 10;
503 : : }
504 : : try
505 : : {
506 : : /* Construct from the parts to avoid the GncRational constructor's
507 : : * automatic rounding.
508 : : */
509 : 978 : return {static_cast<int64_t>(rr_num), static_cast<int64_t>(rr_den)};
510 : : }
511 : 0 : catch (const std::invalid_argument& err)
512 : : {
513 : 0 : std::ostringstream msg;
514 : 0 : msg << "GncNumeric " << *this
515 : 0 : << " could not be represented as a decimal without rounding.\n";
516 : 0 : throw std::range_error(msg.str());
517 : 0 : }
518 : 0 : catch (const std::overflow_error& err)
519 : : {
520 : 0 : std::ostringstream msg;
521 : 0 : msg << "GncNumeric " << *this
522 : 0 : << " overflows when attempting to convert it to decimal.\n";
523 : 0 : throw std::range_error(msg.str());
524 : 0 : }
525 : 0 : catch (const std::underflow_error& err)
526 : : {
527 : 0 : std::ostringstream msg;
528 : 0 : msg << "GncNumeric " << *this
529 : 0 : << " underflows when attempting to convert it to decimal.\n";
530 : 0 : throw std::range_error(msg.str());
531 : 0 : }
532 : : }
533 : :
534 : : void
535 : 188 : GncNumeric::operator+=(GncNumeric b)
536 : : {
537 : 188 : *this = *this + b;
538 : 188 : }
539 : :
540 : : void
541 : 0 : GncNumeric::operator-=(GncNumeric b)
542 : : {
543 : 0 : *this = *this - b;
544 : 0 : }
545 : :
546 : : void
547 : 0 : GncNumeric::operator*=(GncNumeric b)
548 : : {
549 : 0 : *this = *this * b;
550 : 0 : }
551 : :
552 : : void
553 : 0 : GncNumeric::operator/=(GncNumeric b)
554 : : {
555 : 0 : *this = *this / b;
556 : 0 : }
557 : :
558 : : int
559 : 2680 : GncNumeric::cmp(GncNumeric b)
560 : : {
561 : 2680 : if (m_den == b.denom())
562 : : {
563 : 10 : auto b_num = b.num();
564 : 10 : return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
565 : : }
566 : 2670 : GncRational an(*this), bn(b);
567 : 2670 : return an.cmp(bn);
568 : : }
569 : :
570 : : GncNumeric
571 : 322734 : operator+(GncNumeric a, GncNumeric b)
572 : : {
573 : 322734 : if (a.num() == 0)
574 : 64599 : return b;
575 : 258135 : if (b.num() == 0)
576 : 8037 : return a;
577 : 250098 : GncRational ar(a), br(b);
578 : 250098 : auto rr = ar + br;
579 : 250098 : return static_cast<GncNumeric>(rr);
580 : : }
581 : :
582 : : GncNumeric
583 : 70542 : operator-(GncNumeric a, GncNumeric b)
584 : : {
585 : 70542 : return a + (-b);
586 : : }
587 : :
588 : : GncNumeric
589 : 4282 : operator*(GncNumeric a, GncNumeric b)
590 : : {
591 : 4282 : if (a.num() == 0 || b.num() == 0)
592 : : {
593 : 3189 : GncNumeric retval;
594 : 3189 : return retval;
595 : : }
596 : 1093 : GncRational ar(a), br(b);
597 : 1093 : auto rr = ar * br;
598 : 1093 : return static_cast<GncNumeric>(rr);
599 : : }
600 : :
601 : : GncNumeric
602 : 1298 : operator/(GncNumeric a, GncNumeric b)
603 : : {
604 : 1298 : if (a.num() == 0)
605 : : {
606 : 10 : GncNumeric retval;
607 : 10 : return retval;
608 : : }
609 : 1288 : if (b.num() == 0)
610 : 1 : throw std::underflow_error("Attempt to divide by zero.");
611 : :
612 : 1287 : GncRational ar(a), br(b);
613 : 1287 : auto rr = ar / br;
614 : 1287 : return static_cast<GncNumeric>(rr);
615 : : }
616 : :
617 : : template <typename T, typename I> T
618 : 508126 : convert(T num, I new_denom, int how)
619 : : {
620 : 508126 : auto rtype = static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK);
621 : 508126 : unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
622 : :
623 : 508126 : auto dtype = static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK);
624 : 508126 : bool sigfigs = dtype == DenomType::sigfigs;
625 : 508126 : if (dtype == DenomType::reduce)
626 : 740 : num = num.reduce();
627 : :
628 : 508126 : switch (rtype)
629 : : {
630 : 1 : case RoundType::floor:
631 : 1 : if (sigfigs)
632 : 0 : return num.template convert_sigfigs<RoundType::floor>(figs);
633 : : else
634 : 1 : return num.template convert<RoundType::floor>(new_denom);
635 : 1 : case RoundType::ceiling:
636 : 1 : if (sigfigs)
637 : 0 : return num.template convert_sigfigs<RoundType::ceiling>(figs);
638 : : else
639 : 1 : return num.template convert<RoundType::ceiling>(new_denom);
640 : 65337 : case RoundType::truncate:
641 : 65337 : if (sigfigs)
642 : 0 : return num.template convert_sigfigs<RoundType::truncate>(figs);
643 : : else
644 : 65337 : return num.template convert<RoundType::truncate>(new_denom);
645 : 0 : case RoundType::promote:
646 : 0 : if (sigfigs)
647 : 0 : return num.template convert_sigfigs<RoundType::promote>(figs);
648 : : else
649 : 0 : return num.template convert<RoundType::promote>(new_denom);
650 : 0 : case RoundType::half_down:
651 : 0 : if (sigfigs)
652 : 0 : return num.template convert_sigfigs<RoundType::half_down>(figs);
653 : : else
654 : 0 : return num.template convert<RoundType::half_down>(new_denom);
655 : 42595 : case RoundType::half_up:
656 : 42595 : if (sigfigs)
657 : 25 : return num.template convert_sigfigs<RoundType::half_up>(figs);
658 : : else
659 : 42570 : return num.template convert<RoundType::half_up>(new_denom);
660 : 80587 : case RoundType::bankers:
661 : 80587 : if (sigfigs)
662 : 8 : return num.template convert_sigfigs<RoundType::bankers>(figs);
663 : : else
664 : 80579 : return num.template convert<RoundType::bankers>(new_denom);
665 : 314128 : case RoundType::never:
666 : 314128 : if (sigfigs)
667 : 0 : return num.template convert_sigfigs<RoundType::never>(figs);
668 : : else
669 : 314128 : return num.template convert<RoundType::never>(new_denom);
670 : 5477 : default:
671 : : /* round-truncate just returns the numerator unchanged. The old
672 : : * gnc-numeric convert had no "default" behavior at rounding that
673 : : * had the same result, but we need to make it explicit here to
674 : : * run the rest of the conversion code.
675 : : */
676 : 5477 : if (sigfigs)
677 : 0 : return num.template convert_sigfigs<RoundType::truncate>(figs);
678 : : else
679 : 5477 : return num.template convert<RoundType::truncate>(new_denom);
680 : :
681 : : }
682 : : }
683 : :
684 : : /* =============================================================== */
685 : : /* This function is small, simple, and used everywhere below,
686 : : * lets try to inline it.
687 : : */
688 : : GNCNumericErrorCode
689 : 1759285 : gnc_numeric_check(gnc_numeric in)
690 : : {
691 : 1759285 : if (G_LIKELY(in.denom != 0))
692 : : {
693 : 1759115 : return GNC_ERROR_OK;
694 : : }
695 : 170 : else if (in.num)
696 : : {
697 : 129 : if ((0 < in.num) || (-4 > in.num))
698 : : {
699 : 6 : in.num = (gint64) GNC_ERROR_OVERFLOW;
700 : : }
701 : 129 : return (GNCNumericErrorCode) in.num;
702 : : }
703 : : else
704 : : {
705 : 41 : return GNC_ERROR_ARG;
706 : : }
707 : : }
708 : :
709 : :
710 : : /* *******************************************************************
711 : : * gnc_numeric_zero_p
712 : : ********************************************************************/
713 : :
714 : : gboolean
715 : 186951 : gnc_numeric_zero_p(gnc_numeric a)
716 : : {
717 : 186951 : if (gnc_numeric_check(a))
718 : : {
719 : 1 : return 0;
720 : : }
721 : : else
722 : : {
723 : 186950 : if ((a.num == 0) && (a.denom != 0))
724 : : {
725 : 127440 : return 1;
726 : : }
727 : : else
728 : : {
729 : 59510 : return 0;
730 : : }
731 : : }
732 : : }
733 : :
734 : : /* *******************************************************************
735 : : * gnc_numeric_negative_p
736 : : ********************************************************************/
737 : :
738 : : gboolean
739 : 213966 : gnc_numeric_negative_p(gnc_numeric a)
740 : : {
741 : 213966 : if (gnc_numeric_check(a))
742 : : {
743 : 0 : return 0;
744 : : }
745 : : else
746 : : {
747 : 213966 : if ((a.num < 0) && (a.denom != 0))
748 : : {
749 : 41144 : return 1;
750 : : }
751 : : else
752 : : {
753 : 172822 : return 0;
754 : : }
755 : : }
756 : : }
757 : :
758 : : /* *******************************************************************
759 : : * gnc_numeric_positive_p
760 : : ********************************************************************/
761 : :
762 : : gboolean
763 : 2220 : gnc_numeric_positive_p(gnc_numeric a)
764 : : {
765 : 2220 : if (gnc_numeric_check(a))
766 : : {
767 : 0 : return 0;
768 : : }
769 : : else
770 : : {
771 : 2220 : if ((a.num > 0) && (a.denom != 0))
772 : : {
773 : 1352 : return 1;
774 : : }
775 : : else
776 : : {
777 : 868 : return 0;
778 : : }
779 : : }
780 : : }
781 : :
782 : :
783 : : /* *******************************************************************
784 : : * gnc_numeric_compare
785 : : * returns 1 if a>b, -1 if b>a, 0 if a == b
786 : : ********************************************************************/
787 : :
788 : : int
789 : 24137 : gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
790 : : {
791 : 24137 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
792 : : {
793 : 0 : return 0;
794 : : }
795 : :
796 : 24137 : if (a.denom == b.denom)
797 : : {
798 : 21467 : if (a.num == b.num) return 0;
799 : 10335 : if (a.num > b.num) return 1;
800 : 4453 : return -1;
801 : : }
802 : :
803 : 2670 : GncNumeric an (a), bn (b);
804 : :
805 : 2670 : return an.cmp(bn);
806 : : }
807 : :
808 : :
809 : : /* *******************************************************************
810 : : * gnc_numeric_eq
811 : : ********************************************************************/
812 : :
813 : : gboolean
814 : 3397 : gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
815 : : {
816 : 3397 : return ((a.num == b.num) && (a.denom == b.denom));
817 : : }
818 : :
819 : :
820 : : /* *******************************************************************
821 : : * gnc_numeric_equal
822 : : ********************************************************************/
823 : :
824 : : gboolean
825 : 11573 : gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
826 : : {
827 : 11573 : if (gnc_numeric_check(a))
828 : : {
829 : : /* a is not a valid number, check b */
830 : 0 : if (gnc_numeric_check(b))
831 : : /* Both invalid, consider them equal */
832 : 0 : return TRUE;
833 : : else
834 : : /* a invalid, b valid */
835 : 0 : return FALSE;
836 : : }
837 : 11573 : if (gnc_numeric_check(b))
838 : : /* a valid, b invalid */
839 : 0 : return FALSE;
840 : :
841 : 11573 : return gnc_numeric_compare (a, b) == 0;
842 : : }
843 : :
844 : :
845 : : /* *******************************************************************
846 : : * gnc_numeric_same
847 : : * would a and b be equal() if they were both converted to the same
848 : : * denominator?
849 : : ********************************************************************/
850 : :
851 : : int
852 : 4951 : gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
853 : : gint how)
854 : : {
855 : : gnc_numeric aconv, bconv;
856 : :
857 : 4951 : aconv = gnc_numeric_convert(a, denom, how);
858 : 4951 : bconv = gnc_numeric_convert(b, denom, how);
859 : :
860 : 9902 : return(gnc_numeric_equal(aconv, bconv));
861 : : }
862 : :
863 : : static int64_t
864 : 339629 : denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
865 : : {
866 : 339629 : if (denom == GNC_DENOM_AUTO &&
867 : 330843 : (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
868 : : {
869 : 734 : GncInt128 ad(a.denom), bd(b.denom);
870 : 734 : denom = static_cast<int64_t>(ad.lcm(bd));
871 : : }
872 : 339629 : return denom;
873 : : }
874 : :
875 : : /* *******************************************************************
876 : : * gnc_numeric_add
877 : : ********************************************************************/
878 : :
879 : : gnc_numeric
880 : 259009 : gnc_numeric_add(gnc_numeric a, gnc_numeric b,
881 : : gint64 denom, gint how)
882 : : {
883 : 259009 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
884 : : {
885 : 3 : return gnc_numeric_error(GNC_ERROR_ARG);
886 : : }
887 : : try
888 : : {
889 : 259006 : denom = denom_lcd(a, b, denom, how);
890 : 259006 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
891 : : {
892 : 252004 : GncNumeric an (a), bn (b);
893 : 252004 : GncNumeric sum = an + bn;
894 : 252004 : return static_cast<gnc_numeric>(convert(sum, denom, how));
895 : : }
896 : 7002 : GncRational ar(a), br(b);
897 : 7002 : auto sum = ar + br;
898 : 7002 : if (denom == GNC_DENOM_AUTO &&
899 : 6428 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
900 : 6428 : return static_cast<gnc_numeric>(sum.round_to_numeric());
901 : 574 : sum = convert(sum, denom, how);
902 : 574 : if (sum.is_big() || !sum.valid())
903 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
904 : 574 : return static_cast<gnc_numeric>(sum);
905 : : }
906 : 0 : catch (const std::overflow_error& err)
907 : : {
908 : 0 : PWARN("%s", err.what());
909 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
910 : 0 : }
911 : 0 : catch (const std::invalid_argument& err)
912 : : {
913 : 0 : PWARN("%s", err.what());
914 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
915 : 0 : }
916 : 0 : catch (const std::underflow_error& err)
917 : : {
918 : 0 : PWARN("%s", err.what());
919 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
920 : 0 : }
921 : 0 : catch (const std::domain_error& err)
922 : : {
923 : 0 : PWARN("%s", err.what());
924 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
925 : 0 : }
926 : : }
927 : :
928 : : /* *******************************************************************
929 : : * gnc_numeric_sub
930 : : ********************************************************************/
931 : :
932 : : gnc_numeric
933 : 70549 : gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
934 : : gint64 denom, gint how)
935 : : {
936 : 70549 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
937 : : {
938 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
939 : : }
940 : : try
941 : : {
942 : 70549 : denom = denom_lcd(a, b, denom, how);
943 : 70549 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
944 : : {
945 : 70542 : GncNumeric an (a), bn (b);
946 : 70542 : auto sum = an - bn;
947 : 70542 : return static_cast<gnc_numeric>(convert(sum, denom, how));
948 : : }
949 : 7 : GncRational ar(a), br(b);
950 : 7 : auto sum = ar - br;
951 : 7 : if (denom == GNC_DENOM_AUTO &&
952 : 7 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
953 : 7 : return static_cast<gnc_numeric>(sum.round_to_numeric());
954 : 0 : sum = convert(sum, denom, how);
955 : 0 : if (sum.is_big() || !sum.valid())
956 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
957 : 0 : return static_cast<gnc_numeric>(sum);
958 : : }
959 : 0 : catch (const std::overflow_error& err)
960 : : {
961 : 0 : PWARN("%s", err.what());
962 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
963 : 0 : }
964 : 0 : catch (const std::invalid_argument& err)
965 : : {
966 : 0 : PWARN("%s", err.what());
967 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
968 : 0 : }
969 : 0 : catch (const std::underflow_error& err)
970 : : {
971 : 0 : PWARN("%s", err.what());
972 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
973 : 0 : }
974 : 0 : catch (const std::domain_error& err)
975 : : {
976 : 0 : PWARN("%s", err.what());
977 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
978 : 0 : }
979 : : }
980 : :
981 : : /* *******************************************************************
982 : : * gnc_numeric_mul
983 : : ********************************************************************/
984 : :
985 : : gnc_numeric
986 : 8293 : gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
987 : : gint64 denom, gint how)
988 : : {
989 : 8293 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
990 : : {
991 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
992 : : }
993 : :
994 : : try
995 : : {
996 : 8293 : denom = denom_lcd(a, b, denom, how);
997 : 8293 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
998 : : {
999 : 4282 : GncNumeric an (a), bn (b);
1000 : 4282 : auto prod = an * bn;
1001 : 4254 : return static_cast<gnc_numeric>(convert(prod, denom, how));
1002 : : }
1003 : 4011 : GncRational ar(a), br(b);
1004 : 4011 : auto prod = ar * br;
1005 : 4011 : if (denom == GNC_DENOM_AUTO &&
1006 : 24 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
1007 : 16 : return static_cast<gnc_numeric>(prod.round_to_numeric());
1008 : 3995 : prod = convert(prod, denom, how);
1009 : 3995 : if (prod.is_big() || !prod.valid())
1010 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1011 : 3995 : return static_cast<gnc_numeric>(prod);
1012 : : }
1013 : 43 : catch (const std::overflow_error& err)
1014 : : {
1015 : 43 : PWARN("%s", err.what());
1016 : 43 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1017 : 43 : }
1018 : 0 : catch (const std::invalid_argument& err)
1019 : : {
1020 : 0 : PWARN("%s", err.what());
1021 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1022 : 0 : }
1023 : 0 : catch (const std::underflow_error& err)
1024 : : {
1025 : 0 : PWARN("%s", err.what());
1026 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1027 : 0 : }
1028 : 0 : catch (const std::domain_error& err)
1029 : : {
1030 : 0 : PWARN("%s", err.what());
1031 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
1032 : 0 : }
1033 : : }
1034 : :
1035 : :
1036 : : /* *******************************************************************
1037 : : * gnc_numeric_div
1038 : : ********************************************************************/
1039 : :
1040 : : gnc_numeric
1041 : 1785 : gnc_numeric_div(gnc_numeric a, gnc_numeric b,
1042 : : gint64 denom, gint how)
1043 : : {
1044 : 1785 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
1045 : : {
1046 : 4 : return gnc_numeric_error(GNC_ERROR_ARG);
1047 : : }
1048 : : try
1049 : : {
1050 : 1781 : denom = denom_lcd(a, b, denom, how);
1051 : 1781 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
1052 : : {
1053 : 1298 : GncNumeric an (a), bn (b);
1054 : 1298 : auto quot = an / bn;
1055 : 1297 : return static_cast<gnc_numeric>(convert(quot, denom, how));
1056 : : }
1057 : 483 : GncRational ar(a), br(b);
1058 : 483 : auto quot = ar / br;
1059 : 482 : if (denom == GNC_DENOM_AUTO &&
1060 : 482 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
1061 : 47 : return static_cast<gnc_numeric>(quot.round_to_numeric());
1062 : 435 : quot = static_cast<gnc_numeric>(convert(quot, denom, how));
1063 : 435 : if (quot.is_big() || !quot.valid())
1064 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1065 : 435 : return static_cast<gnc_numeric>(quot);
1066 : : }
1067 : 2 : catch (const std::overflow_error& err)
1068 : : {
1069 : 0 : PWARN("%s", err.what());
1070 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1071 : 0 : }
1072 : 0 : catch (const std::invalid_argument& err)
1073 : : {
1074 : 0 : PWARN("%s", err.what());
1075 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1076 : 0 : }
1077 : 2 : catch (const std::underflow_error& err) //Divide by zero
1078 : : {
1079 : 2 : PWARN("%s", err.what());
1080 : 2 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1081 : 2 : }
1082 : 0 : catch (const std::domain_error& err)
1083 : : {
1084 : 0 : PWARN("%s", err.what());
1085 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
1086 : 0 : }
1087 : : }
1088 : :
1089 : : /* *******************************************************************
1090 : : * gnc_numeric_neg
1091 : : * negate the argument
1092 : : ********************************************************************/
1093 : :
1094 : : gnc_numeric
1095 : 2000 : gnc_numeric_neg(gnc_numeric a)
1096 : : {
1097 : 2000 : if (gnc_numeric_check(a))
1098 : : {
1099 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1100 : : }
1101 : 2000 : return gnc_numeric_create(- a.num, a.denom);
1102 : : }
1103 : :
1104 : : /* *******************************************************************
1105 : : * gnc_numeric_abs
1106 : : * return the absolute value of the argument
1107 : : ********************************************************************/
1108 : :
1109 : : gnc_numeric
1110 : 66821 : gnc_numeric_abs(gnc_numeric a)
1111 : : {
1112 : 66821 : if (gnc_numeric_check(a))
1113 : : {
1114 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1115 : : }
1116 : 66821 : return gnc_numeric_create(ABS(a.num), a.denom);
1117 : : }
1118 : :
1119 : :
1120 : : /* *******************************************************************
1121 : : * gnc_numeric_convert
1122 : : ********************************************************************/
1123 : :
1124 : : gnc_numeric
1125 : 174675 : gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
1126 : : {
1127 : 174675 : if (gnc_numeric_check(in))
1128 : 0 : return in;
1129 : : try
1130 : : {
1131 : 174675 : return convert(GncNumeric(in), denom, how);
1132 : : }
1133 : 0 : catch (const std::invalid_argument& err)
1134 : : {
1135 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1136 : 0 : }
1137 : 0 : catch (const std::overflow_error& err)
1138 : : {
1139 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1140 : 0 : }
1141 : 0 : catch (const std::underflow_error& err)
1142 : : {
1143 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1144 : 0 : }
1145 : 0 : catch (const std::domain_error& err)
1146 : : {
1147 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
1148 : 0 : }
1149 : : }
1150 : :
1151 : :
1152 : : /* *******************************************************************
1153 : : * reduce a fraction by GCF elimination. This is NOT done as a
1154 : : * part of the arithmetic API unless GNC_HOW_DENOM_REDUCE is specified
1155 : : * as the output denominator.
1156 : : ********************************************************************/
1157 : :
1158 : : gnc_numeric
1159 : 3291 : gnc_numeric_reduce(gnc_numeric in)
1160 : : {
1161 : 3291 : if (gnc_numeric_check(in))
1162 : : {
1163 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1164 : : }
1165 : :
1166 : 3291 : if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
1167 : 0 : return in;
1168 : : try
1169 : : {
1170 : 3291 : GncNumeric an (in);
1171 : 3291 : return static_cast<gnc_numeric>(an.reduce());
1172 : : }
1173 : 0 : catch (const std::overflow_error& err)
1174 : : {
1175 : 0 : PWARN("%s", err.what());
1176 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1177 : 0 : }
1178 : 0 : catch (const std::invalid_argument& err)
1179 : : {
1180 : 0 : PWARN("%s", err.what());
1181 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1182 : 0 : }
1183 : 0 : catch (const std::underflow_error& err)
1184 : : {
1185 : 0 : PWARN("%s", err.what());
1186 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1187 : 0 : }
1188 : 0 : catch (const std::domain_error& err)
1189 : : {
1190 : 0 : PWARN("%s", err.what());
1191 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
1192 : 0 : }
1193 : : }
1194 : :
1195 : :
1196 : : /* *******************************************************************
1197 : : * gnc_numeric_to_decimal
1198 : : *
1199 : : * Attempt to convert the denominator to an exact power of ten without
1200 : : * rounding. TRUE is returned if 'a' has been converted or was already
1201 : : * decimal. Otherwise, FALSE is returned and 'a' remains unchanged.
1202 : : * The 'max_decimal_places' parameter may be NULL.
1203 : : ********************************************************************/
1204 : :
1205 : : gboolean
1206 : 129626 : gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
1207 : : {
1208 : 129626 : int max_places = max_decimal_places == NULL ? max_leg_digits :
1209 : : *max_decimal_places;
1210 : 129626 : if (a->num == 0) return TRUE;
1211 : : try
1212 : : {
1213 : 32061 : GncNumeric an (*a);
1214 : 32061 : auto bn = an.to_decimal(max_places);
1215 : 31975 : *a = static_cast<gnc_numeric>(bn);
1216 : 31975 : return TRUE;
1217 : : }
1218 : 86 : catch (const std::exception& err)
1219 : : {
1220 : 86 : PINFO ("%s", err.what());
1221 : 86 : return FALSE;
1222 : 86 : }
1223 : : }
1224 : :
1225 : :
1226 : : gnc_numeric
1227 : 708 : gnc_numeric_invert(gnc_numeric num)
1228 : : {
1229 : 708 : if (num.num == 0)
1230 : 0 : return gnc_numeric_zero();
1231 : : try
1232 : : {
1233 : 708 : return static_cast<gnc_numeric>(GncNumeric(num).inv());
1234 : : }
1235 : 0 : catch (const std::overflow_error& err)
1236 : : {
1237 : 0 : PWARN("%s", err.what());
1238 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1239 : 0 : }
1240 : 0 : catch (const std::invalid_argument& err)
1241 : : {
1242 : 0 : PWARN("%s", err.what());
1243 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1244 : 0 : }
1245 : 0 : catch (const std::underflow_error& err)
1246 : : {
1247 : 0 : PWARN("%s", err.what());
1248 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1249 : 0 : }
1250 : 0 : catch (const std::domain_error& err)
1251 : : {
1252 : 0 : PWARN("%s", err.what());
1253 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
1254 : 0 : }
1255 : : }
1256 : :
1257 : : /* *******************************************************************
1258 : : * double_to_gnc_numeric
1259 : : ********************************************************************/
1260 : :
1261 : : #ifdef _MSC_VER
1262 : : # define rint /* */
1263 : : #endif
1264 : : gnc_numeric
1265 : 350 : double_to_gnc_numeric(double in, gint64 denom, gint how)
1266 : : {
1267 : : try
1268 : : {
1269 : 350 : GncNumeric an(in);
1270 : 350 : return convert(an, denom, how);
1271 : : }
1272 : 0 : catch (const std::overflow_error& err)
1273 : : {
1274 : 0 : PWARN("%s", err.what());
1275 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1276 : 0 : }
1277 : 0 : catch (const std::invalid_argument& err)
1278 : : {
1279 : 0 : PWARN("%s", err.what());
1280 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1281 : 0 : }
1282 : 0 : catch (const std::underflow_error& err)
1283 : : {
1284 : 0 : PWARN("%s", err.what());
1285 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1286 : 0 : }
1287 : 0 : catch (const std::domain_error& err)
1288 : : {
1289 : 0 : PWARN("%s", err.what());
1290 : 0 : return gnc_numeric_error(GNC_ERROR_REMAINDER);
1291 : 0 : }
1292 : : }
1293 : :
1294 : : /* *******************************************************************
1295 : : * gnc_numeric_to_double
1296 : : ********************************************************************/
1297 : :
1298 : : double
1299 : 156 : gnc_numeric_to_double(gnc_numeric in)
1300 : : {
1301 : 156 : if (in.denom > 0)
1302 : : {
1303 : 133 : return (double)in.num / (double)in.denom;
1304 : : }
1305 : : else
1306 : : {
1307 : 23 : return (double)(in.num * -in.denom);
1308 : : }
1309 : : }
1310 : :
1311 : : /* *******************************************************************
1312 : : * gnc_numeric_error
1313 : : ********************************************************************/
1314 : :
1315 : : gnc_numeric
1316 : 323 : gnc_numeric_error(GNCNumericErrorCode error_code)
1317 : : {
1318 : 323 : return gnc_numeric_create(error_code, 0LL);
1319 : : }
1320 : :
1321 : :
1322 : :
1323 : : /* *******************************************************************
1324 : : * gnc_numeric text IO
1325 : : ********************************************************************/
1326 : :
1327 : : gchar *
1328 : 8138 : gnc_numeric_to_string(gnc_numeric n)
1329 : : {
1330 : : char *result;
1331 : 8138 : int64_t tmpnum = n.num;
1332 : 8138 : int64_t tmpdenom = n.denom;
1333 : :
1334 : 8138 : result = g_strdup_printf("%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
1335 : :
1336 : 8138 : return result;
1337 : : }
1338 : :
1339 : : gchar *
1340 : 36 : gnc_num_dbg_to_string(gnc_numeric n)
1341 : : {
1342 : : static char buff[1000];
1343 : : static char *p = buff;
1344 : : static const size_t size = 50;
1345 : 36 : int64_t tmpnum = n.num;
1346 : 36 : int64_t tmpdenom = n.denom;
1347 : :
1348 : 36 : p += size;
1349 : 36 : if ((size_t)(p - buff) > (sizeof(buff) - size))
1350 : 1 : p = buff;
1351 : :
1352 : 36 : snprintf(p, size, "%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
1353 : :
1354 : 36 : return p;
1355 : : }
1356 : :
1357 : : gnc_numeric
1358 : 5144 : gnc_numeric_from_string (const gchar* str)
1359 : : {
1360 : 5144 : if (!str)
1361 : 0 : return gnc_numeric_error (GNC_ERROR_ARG);
1362 : :
1363 : : // the default gnc_numeric string format is "num/denom", whereby
1364 : : // the denom must be >= 1. this speedily parses it. this also
1365 : : // parses "num" as num/1.
1366 : 5144 : if (auto res = fast_numeral_rational (str))
1367 : 5135 : return *res;
1368 : :
1369 : : try
1370 : : {
1371 : 18 : return GncNumeric (str);
1372 : : }
1373 : 2 : catch (const std::exception& err)
1374 : : {
1375 : 2 : PWARN("%s", err.what());
1376 : 2 : return gnc_numeric_error (GNC_ERROR_ARG);
1377 : 2 : }
1378 : : }
1379 : :
1380 : : /* *******************************************************************
1381 : : * GValue handling
1382 : : ********************************************************************/
1383 : : static gnc_numeric*
1384 : 513 : gnc_numeric_boxed_copy_func( gnc_numeric *in_gnc_numeric )
1385 : : {
1386 : 513 : if (!in_gnc_numeric)
1387 : 0 : return nullptr;
1388 : :
1389 : : /* newvalue will be passed to g_free so we must allocate with g_malloc. */
1390 : 513 : auto newvalue = static_cast<gnc_numeric*>(g_malloc (sizeof (gnc_numeric)));
1391 : 513 : *newvalue = *in_gnc_numeric;
1392 : :
1393 : 513 : return newvalue;
1394 : : }
1395 : :
1396 : : static void
1397 : 461 : gnc_numeric_boxed_free_func( gnc_numeric *in_gnc_numeric )
1398 : : {
1399 : 461 : g_free( in_gnc_numeric );
1400 : 461 : }
1401 : :
1402 : 4845 : G_DEFINE_BOXED_TYPE (gnc_numeric, gnc_numeric, gnc_numeric_boxed_copy_func, gnc_numeric_boxed_free_func)
1403 : :
1404 : : /* *******************************************************************
1405 : : * gnc_numeric misc testing
1406 : : ********************************************************************/
1407 : : #ifdef _GNC_NUMERIC_TEST
1408 : :
1409 : : static char *
1410 : : gnc_numeric_print(gnc_numeric in)
1411 : : {
1412 : : char * retval;
1413 : : if (gnc_numeric_check(in))
1414 : : {
1415 : : retval = g_strdup_printf("<ERROR> [%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
1416 : : in.num,
1417 : : in.denom);
1418 : : }
1419 : : else
1420 : : {
1421 : : retval = g_strdup_printf("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]",
1422 : : in.num,
1423 : : in.denom);
1424 : : }
1425 : : return retval;
1426 : : }
1427 : :
1428 : : int
1429 : : main(int argc, char ** argv)
1430 : : {
1431 : : gnc_numeric a = gnc_numeric_create(1, 3);
1432 : : gnc_numeric b = gnc_numeric_create(1, 4);
1433 : : gnc_numeric c;
1434 : :
1435 : : gnc_numeric err;
1436 : :
1437 : :
1438 : : printf("multiply (EXACT): %s * %s = %s\n",
1439 : : gnc_numeric_print(a), gnc_numeric_print(b),
1440 : : gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT)));
1441 : :
1442 : : printf("multiply (REDUCE): %s * %s = %s\n",
1443 : : gnc_numeric_print(a), gnc_numeric_print(b),
1444 : : gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE)));
1445 : :
1446 : :
1447 : : return 0;
1448 : : }
1449 : : #endif
1450 : :
1451 : :
1452 : : std::ostream&
1453 : 284 : operator<<(std::ostream& s, GncNumeric n)
1454 : : {
1455 : : using boost::locale::conv::utf_to_utf;
1456 : 284 : std::basic_ostringstream<wchar_t> ss;
1457 : 284 : ss.imbue(s.getloc());
1458 : 284 : ss << n;
1459 : 284 : s << utf_to_utf<char>(ss.str());
1460 : 284 : return s;
1461 : 284 : }
1462 : :
1463 : 6 : const char* gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
1464 : : {
1465 : 6 : switch (error_code)
1466 : : {
1467 : 6 : case GNC_ERROR_OK:
1468 : 6 : return "GNC_ERROR_OK";
1469 : 0 : case GNC_ERROR_ARG:
1470 : 0 : return "GNC_ERROR_ARG";
1471 : 0 : case GNC_ERROR_OVERFLOW:
1472 : 0 : return "GNC_ERROR_OVERFLOW";
1473 : 0 : case GNC_ERROR_DENOM_DIFF:
1474 : 0 : return "GNC_ERROR_DENOM_DIFF";
1475 : 0 : case GNC_ERROR_REMAINDER:
1476 : 0 : return "GNC_ERROR_REMAINDER";
1477 : 0 : default:
1478 : 0 : return "<unknown>";
1479 : : }
1480 : : }
1481 : :
1482 : : /* ======================== END OF FILE =================== */
|