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