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/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 : 51452 : powten (unsigned int exp)
65 : : {
66 : 51452 : if (exp > max_leg_digits)
67 : 1037 : exp = max_leg_digits;
68 : 51452 : return pten[exp];
69 : : }
70 : :
71 : 267497 : GncNumeric::GncNumeric(GncRational rr)
72 : : {
73 : : /* Can't use isValid here because we want to throw different exceptions. */
74 : 267497 : if (rr.num().isNan() || rr.denom().isNan())
75 : 0 : throw std::underflow_error("Operation resulted in NaN.");
76 : 267497 : if (rr.num().isOverflow() || rr.denom().isOverflow())
77 : 0 : throw std::overflow_error("Operation overflowed a 128-bit int.");
78 : 267497 : 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 : 267469 : m_num = static_cast<int64_t>(rr.num());
84 : 267469 : m_den = static_cast<int64_t>(rr.denom());
85 : 267469 : }
86 : :
87 : 369 : GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
88 : : {
89 : : static uint64_t max_leg_value{INT64_C(1000000000000000000)};
90 : 369 : 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 : 369 : constexpr auto max_num = static_cast<double>(INT64_MAX);
97 : 369 : auto logval = log10(fabs(d));
98 : : int64_t den;
99 : : uint8_t den_digits;
100 : 369 : if (logval > 0.0)
101 : 340 : den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval));
102 : : else
103 : 29 : den_digits = max_leg_digits;
104 : 369 : den = powten(den_digits);
105 : 369 : auto num_d = static_cast<double>(den) * d;
106 : 653 : while (fabs(num_d) > max_num && den_digits > 1)
107 : : {
108 : 284 : den = powten(--den_digits);
109 : 284 : num_d = static_cast<double>(den) * d;
110 : : }
111 : 369 : auto num = static_cast<int64_t>(floor(num_d));
112 : :
113 : 369 : if (num == 0)
114 : 6 : return;
115 : 363 : GncNumeric q(num, den);
116 : 363 : auto r = q.reduce();
117 : 363 : m_num = r.num();
118 : 363 : 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 : 5162 : fast_numeral_rational (const char* str)
218 : : {
219 : 5162 : 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 : 5161 : auto end_ptr{(const char*)memchr (str, '\0', 48)};
226 : 5161 : if (!end_ptr)
227 : 0 : return {};
228 : :
229 : 5161 : int64_t num, denom{};
230 : 5161 : auto result = std::from_chars (str, end_ptr, num);
231 : 5161 : if (result.ec != std::errc())
232 : 6 : return {};
233 : :
234 : 5155 : if (result.ptr == end_ptr)
235 : 1 : return gnc_numeric_create (num, 1);
236 : :
237 : 5154 : if (*result.ptr != '/')
238 : 20 : return {};
239 : :
240 : 5134 : result = std::from_chars (result.ptr + 1, end_ptr, denom);
241 : 5134 : if (result.ec != std::errc() || result.ptr != end_ptr || denom <= 0)
242 : 0 : return {};
243 : :
244 : 5134 : 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 : : 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 : : 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 : 550418 : GncNumeric::operator gnc_numeric() const noexcept
405 : : {
406 : 550418 : 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 : 70586 : GncNumeric::operator-() const noexcept
416 : : {
417 : 70586 : GncNumeric b(*this);
418 : 70586 : b.m_num = - b.m_num;
419 : 70586 : return b;
420 : : }
421 : :
422 : : GncNumeric
423 : 708 : GncNumeric::inv() const noexcept
424 : : {
425 : 708 : if (m_num == 0)
426 : 0 : return *this;
427 : 708 : if (m_num < 0)
428 : 0 : return GncNumeric(-m_den, -m_num);
429 : 708 : 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 : 4394 : GncNumeric::reduce() const noexcept
442 : : {
443 : 4394 : return static_cast<GncNumeric>(GncRational(*this).reduce());
444 : : }
445 : :
446 : : GncNumeric::round_param
447 : 514282 : GncNumeric::prepare_conversion(int64_t new_denom) const
448 : : {
449 : 514282 : if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
450 : 422494 : return {m_num, m_den, 0};
451 : 91788 : GncRational conversion(new_denom, m_den);
452 : 91788 : auto red_conv = conversion.reduce();
453 : 91788 : GncInt128 old_num(m_num);
454 : 91788 : auto new_num = old_num * red_conv.num();
455 : 91788 : auto rem = new_num % red_conv.denom();
456 : 91788 : new_num /= red_conv.denom();
457 : 91788 : 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 : 91773 : return {static_cast<int64_t>(new_num),
465 : 91773 : static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
466 : : }
467 : :
468 : : int64_t
469 : 33 : GncNumeric::sigfigs_denom(unsigned figs) const noexcept
470 : : {
471 : 33 : if (m_num == 0)
472 : 4 : return 1;
473 : :
474 : 29 : int64_t num_abs{std::abs(m_num)};
475 : 29 : bool not_frac = num_abs > m_den;
476 : 29 : int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
477 : 29 : unsigned digits{};
478 : 44 : while (val >= 10)
479 : : {
480 : 15 : ++digits;
481 : 15 : val /= 10;
482 : : }
483 : 29 : return not_frac ?
484 : 23 : powten(digits < figs ? figs - digits - 1 : 0) :
485 : 29 : 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 : 32397 : GncNumeric::is_decimal() const noexcept
498 : : {
499 : 65941 : for (unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
500 : : {
501 : 65166 : if (m_den == pten[pwr])
502 : 31330 : return true;
503 : 33836 : if (m_den % pten[pwr])
504 : 292 : return false;
505 : : }
506 : 775 : return false;
507 : : }
508 : :
509 : : GncNumeric
510 : 32117 : GncNumeric::to_decimal(unsigned int max_places) const
511 : : {
512 : 32117 : if (max_places > max_leg_digits)
513 : 0 : max_places = max_leg_digits;
514 : :
515 : 32117 : if (m_num == 0)
516 : 0 : return GncNumeric();
517 : :
518 : 32117 : if (is_decimal())
519 : : {
520 : 31050 : if (m_num == 0 || m_den < powten(max_places))
521 : 31050 : 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 : 1067 : GncRational rr(*this);
535 : 1067 : 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 : 981 : unsigned int pwr{1};
538 : 18639 : for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
539 : 981 : auto reduce_to = powten(pwr);
540 : 981 : GncInt128 rr_num(rr.num()), rr_den(rr.denom());
541 : 981 : 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 : 16870 : while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
548 : : {
549 : 15889 : rr_num /= 10;
550 : 15889 : rr_den /= 10;
551 : : }
552 : : try
553 : : {
554 : : /* Construct from the parts to avoid the GncRational constructor's
555 : : * automatic rounding.
556 : : */
557 : 981 : 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 : 2678 : GncNumeric::cmp(GncNumeric b)
608 : : {
609 : 2678 : 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 : 2668 : GncRational an(*this), bn(b);
615 : 2668 : return an.cmp(bn);
616 : : }
617 : :
618 : : GncNumeric
619 : 333452 : operator+(GncNumeric a, GncNumeric b)
620 : : {
621 : 333452 : if (a.num() == 0)
622 : 64707 : return b;
623 : 268745 : if (b.num() == 0)
624 : 8037 : return a;
625 : 260708 : GncRational ar(a), br(b);
626 : 260708 : auto rr = ar + br;
627 : 260708 : return static_cast<GncNumeric>(rr);
628 : : }
629 : :
630 : : GncNumeric
631 : 70586 : operator-(GncNumeric a, GncNumeric b)
632 : : {
633 : 70586 : return a + (-b);
634 : : }
635 : :
636 : : GncNumeric
637 : 4282 : operator*(GncNumeric a, GncNumeric b)
638 : : {
639 : 4282 : if (a.num() == 0 || b.num() == 0)
640 : : {
641 : 3189 : GncNumeric retval;
642 : 3189 : return retval;
643 : : }
644 : 1093 : GncRational ar(a), br(b);
645 : 1093 : auto rr = ar * br;
646 : 1093 : return static_cast<GncNumeric>(rr);
647 : : }
648 : :
649 : : GncNumeric
650 : 1298 : operator/(GncNumeric a, GncNumeric b)
651 : : {
652 : 1298 : if (a.num() == 0)
653 : : {
654 : 10 : GncNumeric retval;
655 : 10 : return retval;
656 : : }
657 : 1288 : if (b.num() == 0)
658 : 1 : throw std::underflow_error("Attempt to divide by zero.");
659 : :
660 : 1287 : GncRational ar(a), br(b);
661 : 1287 : auto rr = ar / br;
662 : 1287 : return static_cast<GncNumeric>(rr);
663 : : }
664 : :
665 : : template <typename T, typename I> T
666 : 519286 : convert(T num, I new_denom, int how)
667 : : {
668 : 519286 : auto rtype = static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK);
669 : 519286 : unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
670 : :
671 : 519286 : auto dtype = static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK);
672 : 519286 : bool sigfigs = dtype == DenomType::sigfigs;
673 : 519286 : if (dtype == DenomType::reduce)
674 : 740 : num = num.reduce();
675 : :
676 : 519286 : switch (rtype)
677 : : {
678 : 1 : case RoundType::floor:
679 : 1 : if (sigfigs)
680 : 0 : return num.template convert_sigfigs<RoundType::floor>(figs);
681 : : else
682 : 1 : 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 : 65381 : case RoundType::truncate:
689 : 65381 : if (sigfigs)
690 : 0 : return num.template convert_sigfigs<RoundType::truncate>(figs);
691 : : else
692 : 65381 : 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 : 42919 : case RoundType::half_up:
704 : 42919 : if (sigfigs)
705 : 25 : return num.template convert_sigfigs<RoundType::half_up>(figs);
706 : : else
707 : 42894 : return num.template convert<RoundType::half_up>(new_denom);
708 : 80661 : case RoundType::bankers:
709 : 80661 : if (sigfigs)
710 : 8 : return num.template convert_sigfigs<RoundType::bankers>(figs);
711 : : else
712 : 80653 : return num.template convert<RoundType::bankers>(new_denom);
713 : 324846 : case RoundType::never:
714 : 324846 : if (sigfigs)
715 : 0 : return num.template convert_sigfigs<RoundType::never>(figs);
716 : : else
717 : 324846 : return num.template convert<RoundType::never>(new_denom);
718 : 5477 : 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 : 5477 : if (sigfigs)
725 : 0 : return num.template convert_sigfigs<RoundType::truncate>(figs);
726 : : else
727 : 5477 : 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 : 1782960 : gnc_numeric_check(gnc_numeric in)
738 : : {
739 : 1782960 : if (G_LIKELY(in.denom != 0))
740 : : {
741 : 1782789 : return GNC_ERROR_OK;
742 : : }
743 : 171 : else if (in.num)
744 : : {
745 : 130 : if ((0 < in.num) || (-4 > in.num))
746 : : {
747 : 6 : in.num = (gint64) GNC_ERROR_OVERFLOW;
748 : : }
749 : 130 : return (GNCNumericErrorCode) in.num;
750 : : }
751 : : else
752 : : {
753 : 41 : return GNC_ERROR_ARG;
754 : : }
755 : : }
756 : :
757 : :
758 : : /* *******************************************************************
759 : : * gnc_numeric_zero_p
760 : : ********************************************************************/
761 : :
762 : : gboolean
763 : 187279 : gnc_numeric_zero_p(gnc_numeric a)
764 : : {
765 : 187279 : if (gnc_numeric_check(a))
766 : : {
767 : 1 : return 0;
768 : : }
769 : : else
770 : : {
771 : 187278 : if ((a.num == 0) && (a.denom != 0))
772 : : {
773 : 127646 : return 1;
774 : : }
775 : : else
776 : : {
777 : 59632 : return 0;
778 : : }
779 : : }
780 : : }
781 : :
782 : : /* *******************************************************************
783 : : * gnc_numeric_negative_p
784 : : ********************************************************************/
785 : :
786 : : gboolean
787 : 214206 : gnc_numeric_negative_p(gnc_numeric a)
788 : : {
789 : 214206 : if (gnc_numeric_check(a))
790 : : {
791 : 0 : return 0;
792 : : }
793 : : else
794 : : {
795 : 214206 : if ((a.num < 0) && (a.denom != 0))
796 : : {
797 : 41198 : return 1;
798 : : }
799 : : else
800 : : {
801 : 173008 : return 0;
802 : : }
803 : : }
804 : : }
805 : :
806 : : /* *******************************************************************
807 : : * gnc_numeric_positive_p
808 : : ********************************************************************/
809 : :
810 : : gboolean
811 : 2220 : gnc_numeric_positive_p(gnc_numeric a)
812 : : {
813 : 2220 : if (gnc_numeric_check(a))
814 : : {
815 : 0 : return 0;
816 : : }
817 : : else
818 : : {
819 : 2220 : if ((a.num > 0) && (a.denom != 0))
820 : : {
821 : 1352 : return 1;
822 : : }
823 : : else
824 : : {
825 : 868 : 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 : 24043 : gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
838 : : {
839 : 24043 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
840 : : {
841 : 0 : return 0;
842 : : }
843 : :
844 : 24043 : if (a.denom == b.denom)
845 : : {
846 : 21375 : if (a.num == b.num) return 0;
847 : 10339 : if (a.num > b.num) return 1;
848 : 4593 : return -1;
849 : : }
850 : :
851 : 2668 : GncNumeric an (a), bn (b);
852 : :
853 : 2668 : return an.cmp(bn);
854 : : }
855 : :
856 : :
857 : : /* *******************************************************************
858 : : * gnc_numeric_eq
859 : : ********************************************************************/
860 : :
861 : : gboolean
862 : 3397 : gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
863 : : {
864 : 3397 : return ((a.num == b.num) && (a.denom == b.denom));
865 : : }
866 : :
867 : :
868 : : /* *******************************************************************
869 : : * gnc_numeric_equal
870 : : ********************************************************************/
871 : :
872 : : gboolean
873 : 11627 : gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
874 : : {
875 : 11627 : 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 : 11627 : if (gnc_numeric_check(b))
886 : : /* a valid, b invalid */
887 : 0 : return FALSE;
888 : :
889 : 11627 : 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 : 5005 : gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
901 : : gint how)
902 : : {
903 : : gnc_numeric aconv, bconv;
904 : :
905 : 5005 : aconv = gnc_numeric_convert(a, denom, how);
906 : 5005 : bconv = gnc_numeric_convert(b, denom, how);
907 : :
908 : 10010 : return(gnc_numeric_equal(aconv, bconv));
909 : : }
910 : :
911 : : static int64_t
912 : 350401 : denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
913 : : {
914 : 350401 : if (denom == GNC_DENOM_AUTO &&
915 : 341615 : (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
916 : : {
917 : 734 : GncInt128 ad(a.denom), bd(b.denom);
918 : 734 : denom = static_cast<int64_t>(ad.lcm(bd));
919 : : }
920 : 350401 : return denom;
921 : : }
922 : :
923 : : /* *******************************************************************
924 : : * gnc_numeric_add
925 : : ********************************************************************/
926 : :
927 : : gnc_numeric
928 : 269737 : gnc_numeric_add(gnc_numeric a, gnc_numeric b,
929 : : gint64 denom, gint how)
930 : : {
931 : 269737 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
932 : : {
933 : 3 : return gnc_numeric_error(GNC_ERROR_ARG);
934 : : }
935 : : try
936 : : {
937 : 269734 : denom = denom_lcd(a, b, denom, how);
938 : 269734 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
939 : : {
940 : 262678 : GncNumeric an (a), bn (b);
941 : 262678 : GncNumeric sum = an + bn;
942 : 262678 : return static_cast<gnc_numeric>(convert(sum, denom, how));
943 : : }
944 : 7056 : GncRational ar(a), br(b);
945 : 7056 : auto sum = ar + br;
946 : 7056 : if (denom == GNC_DENOM_AUTO &&
947 : 6482 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
948 : 6482 : return static_cast<gnc_numeric>(sum.round_to_numeric());
949 : 574 : sum = convert(sum, denom, how);
950 : 574 : if (sum.is_big() || !sum.valid())
951 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
952 : 574 : 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 : 70593 : gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
982 : : gint64 denom, gint how)
983 : : {
984 : 70593 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
985 : : {
986 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
987 : : }
988 : : try
989 : : {
990 : 70593 : denom = denom_lcd(a, b, denom, how);
991 : 70593 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
992 : : {
993 : 70586 : GncNumeric an (a), bn (b);
994 : 70586 : auto sum = an - bn;
995 : 70586 : 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 : 8293 : gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
1035 : : gint64 denom, gint how)
1036 : : {
1037 : 8293 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
1038 : : {
1039 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1040 : : }
1041 : :
1042 : : try
1043 : : {
1044 : 8293 : denom = denom_lcd(a, b, denom, how);
1045 : 8293 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
1046 : : {
1047 : 4282 : GncNumeric an (a), bn (b);
1048 : 4282 : auto prod = an * bn;
1049 : 4254 : return static_cast<gnc_numeric>(convert(prod, denom, how));
1050 : : }
1051 : 4011 : GncRational ar(a), br(b);
1052 : 4011 : auto prod = ar * br;
1053 : 4011 : if (denom == GNC_DENOM_AUTO &&
1054 : 24 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
1055 : 16 : return static_cast<gnc_numeric>(prod.round_to_numeric());
1056 : 3995 : prod = convert(prod, denom, how);
1057 : 3995 : if (prod.is_big() || !prod.valid())
1058 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1059 : 3995 : 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 : 1785 : gnc_numeric_div(gnc_numeric a, gnc_numeric b,
1090 : : gint64 denom, gint how)
1091 : : {
1092 : 1785 : if (gnc_numeric_check(a) || gnc_numeric_check(b))
1093 : : {
1094 : 4 : return gnc_numeric_error(GNC_ERROR_ARG);
1095 : : }
1096 : : try
1097 : : {
1098 : 1781 : denom = denom_lcd(a, b, denom, how);
1099 : 1781 : if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
1100 : : {
1101 : 1298 : GncNumeric an (a), bn (b);
1102 : 1298 : auto quot = an / bn;
1103 : 1297 : return static_cast<gnc_numeric>(convert(quot, denom, how));
1104 : : }
1105 : 483 : GncRational ar(a), br(b);
1106 : 483 : auto quot = ar / br;
1107 : 482 : if (denom == GNC_DENOM_AUTO &&
1108 : 482 : (how & GNC_NUMERIC_RND_MASK) != GNC_HOW_RND_NEVER)
1109 : 47 : return static_cast<gnc_numeric>(quot.round_to_numeric());
1110 : 435 : quot = static_cast<gnc_numeric>(convert(quot, denom, how));
1111 : 435 : if (quot.is_big() || !quot.valid())
1112 : 0 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1113 : 435 : return static_cast<gnc_numeric>(quot);
1114 : : }
1115 : 2 : 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 : 2 : catch (const std::underflow_error& err) //Divide by zero
1126 : : {
1127 : 2 : PWARN("%s", err.what());
1128 : 2 : return gnc_numeric_error(GNC_ERROR_OVERFLOW);
1129 : 2 : }
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 : 2054 : gnc_numeric_neg(gnc_numeric a)
1144 : : {
1145 : 2054 : if (gnc_numeric_check(a))
1146 : : {
1147 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1148 : : }
1149 : 2054 : 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 : 66865 : gnc_numeric_abs(gnc_numeric a)
1159 : : {
1160 : 66865 : if (gnc_numeric_check(a))
1161 : : {
1162 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1163 : : }
1164 : 66865 : return gnc_numeric_create(ABS(a.num), a.denom);
1165 : : }
1166 : :
1167 : :
1168 : : /* *******************************************************************
1169 : : * gnc_numeric_convert
1170 : : ********************************************************************/
1171 : :
1172 : : gnc_numeric
1173 : 175105 : gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
1174 : : {
1175 : 175105 : if (gnc_numeric_check(in))
1176 : 0 : return in;
1177 : : try
1178 : : {
1179 : 175105 : 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 : 3291 : gnc_numeric_reduce(gnc_numeric in)
1208 : : {
1209 : 3291 : if (gnc_numeric_check(in))
1210 : : {
1211 : 0 : return gnc_numeric_error(GNC_ERROR_ARG);
1212 : : }
1213 : :
1214 : 3291 : if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
1215 : 0 : return in;
1216 : : try
1217 : : {
1218 : 3291 : GncNumeric an (in);
1219 : 3291 : 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 : 129714 : gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
1255 : : {
1256 : 129714 : int max_places = max_decimal_places == NULL ? max_leg_digits :
1257 : : *max_decimal_places;
1258 : 129714 : if (a->num == 0) return TRUE;
1259 : : try
1260 : : {
1261 : 32117 : GncNumeric an (*a);
1262 : 32117 : auto bn = an.to_decimal(max_places);
1263 : 32031 : *a = static_cast<gnc_numeric>(bn);
1264 : 32031 : return TRUE;
1265 : : }
1266 : 86 : catch (const std::exception& err)
1267 : : {
1268 : 86 : PINFO ("%s", err.what());
1269 : 86 : return FALSE;
1270 : 86 : }
1271 : : }
1272 : :
1273 : :
1274 : : gnc_numeric
1275 : 708 : gnc_numeric_invert(gnc_numeric num)
1276 : : {
1277 : 708 : if (num.num == 0)
1278 : 0 : return gnc_numeric_zero();
1279 : : try
1280 : : {
1281 : 708 : 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 : 362 : double_to_gnc_numeric(double in, gint64 denom, gint how)
1314 : : {
1315 : : try
1316 : : {
1317 : 362 : GncNumeric an(in);
1318 : 362 : 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 : 156 : gnc_numeric_to_double(gnc_numeric in)
1348 : : {
1349 : 156 : if (in.denom > 0)
1350 : : {
1351 : 133 : return (double)in.num / (double)in.denom;
1352 : : }
1353 : : else
1354 : : {
1355 : 23 : return (double)(in.num * -in.denom);
1356 : : }
1357 : : }
1358 : :
1359 : : /* *******************************************************************
1360 : : * gnc_numeric_error
1361 : : ********************************************************************/
1362 : :
1363 : : gnc_numeric
1364 : 324 : gnc_numeric_error(GNCNumericErrorCode error_code)
1365 : : {
1366 : 324 : return gnc_numeric_create(error_code, 0LL);
1367 : : }
1368 : :
1369 : :
1370 : :
1371 : : /* *******************************************************************
1372 : : * gnc_numeric text IO
1373 : : ********************************************************************/
1374 : :
1375 : : gchar *
1376 : 8138 : gnc_numeric_to_string(gnc_numeric n)
1377 : : {
1378 : : char *result;
1379 : 8138 : int64_t tmpnum = n.num;
1380 : 8138 : int64_t tmpdenom = n.denom;
1381 : :
1382 : 8138 : result = g_strdup_printf("%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
1383 : :
1384 : 8138 : 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 : 5144 : gnc_numeric_from_string (const gchar* str)
1407 : : {
1408 : 5144 : 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 : 5144 : if (auto res = fast_numeral_rational (str))
1415 : 5135 : 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 : 513 : gnc_numeric_boxed_copy_func( gnc_numeric *in_gnc_numeric )
1433 : : {
1434 : 513 : 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 : 513 : auto newvalue = static_cast<gnc_numeric*>(g_malloc (sizeof (gnc_numeric)));
1439 : 513 : *newvalue = *in_gnc_numeric;
1440 : :
1441 : 513 : return newvalue;
1442 : : }
1443 : :
1444 : : static void
1445 : 461 : gnc_numeric_boxed_free_func( gnc_numeric *in_gnc_numeric )
1446 : : {
1447 : 461 : g_free( in_gnc_numeric );
1448 : 461 : }
1449 : :
1450 : 4872 : 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 : 6 : const char* gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
1512 : : {
1513 : 6 : switch (error_code)
1514 : : {
1515 : 6 : case GNC_ERROR_OK:
1516 : 6 : return "GNC_ERROR_OK";
1517 : 0 : case GNC_ERROR_ARG:
1518 : 0 : 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 =================== */
|