GnuCash c935c2f+
Loading...
Searching...
No Matches
gnc-numeric.cpp
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
49static QofLogModule log_module = "qof";
50
51static const uint8_t max_leg_digits{18};
52static 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
63int64_t
64powten (unsigned int exp)
65{
66 if (exp > max_leg_digits)
67 exp = max_leg_digits;
68 return pten[exp];
69}
70
72{
73 /* Can't use isValid here because we want to throw different exceptions. */
74 if (rr.num().isNan() || rr.denom().isNan())
75 throw std::underflow_error("Operation resulted in NaN.");
76 if (rr.num().isOverflow() || rr.denom().isOverflow())
77 throw std::overflow_error("Operation overflowed a 128-bit int.");
78 if (rr.num().isBig() || rr.denom().isBig())
79 {
80 GncRational reduced(rr.reduce());
81 rr = reduced.round_to_numeric(); // A no-op if it's already small.
82 }
83 m_num = static_cast<int64_t>(rr.num());
84 m_den = static_cast<int64_t>(rr.denom());
85}
86
87GncNumeric::GncNumeric(double d) : m_num(0), m_den(1)
88{
89 static uint64_t max_leg_value{INT64_C(1000000000000000000)};
90 if (std::isnan(d) || fabs(d) > max_leg_value)
91 {
92 std::ostringstream msg;
93 msg << "Unable to construct a GncNumeric from " << d << ".\n";
94 throw std::invalid_argument(msg.str());
95 }
96 constexpr auto max_num = static_cast<double>(INT64_MAX);
97 auto logval = log10(fabs(d));
98 int64_t den;
99 uint8_t den_digits;
100 if (logval > 0.0)
101 den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval));
102 else
103 den_digits = max_leg_digits;
104 den = powten(den_digits);
105 auto num_d = static_cast<double>(den) * d;
106 while (fabs(num_d) > max_num && den_digits > 1)
107 {
108 den = powten(--den_digits);
109 num_d = static_cast<double>(den) * d;
110 }
111 auto num = static_cast<int64_t>(floor(num_d));
112
113 if (num == 0)
114 return;
115 GncNumeric q(num, den);
116 auto r = q.reduce();
117 m_num = r.num();
118 m_den = r.denom();
119}
120
121using boost::regex;
122using boost::u32regex;
123using boost::regex_search;
124using boost::u32regex_search;
125using boost::smatch;
126
127
128static std::pair<int64_t, int64_t>
129reduce_number_pair(std::pair<GncInt128, GncInt128>num_pair,
130 const std::string& num_str, bool autoround)
131{
132 auto [n, d] = num_pair;
133 if (!autoround && n.isBig()) {
134 std::ostringstream errmsg;
135 errmsg << "Decimal string " << num_str
136 << "can't be represented in a GncNumeric without rounding.";
137 throw std::overflow_error(errmsg.str());
138 }
139 while (n.isBig() && d > 0) {
140 n >>= 1;
141 d >>= 1;
142 }
143 if (n.isBig()) // Shouldn't happen, of course
144 {
145 std::ostringstream errmsg;
146 errmsg << "Decimal string " << num_str
147 << " can't be represented in a GncNumeric, even after reducing "
148 "denom to "
149 << d;
150 throw std::overflow_error(errmsg.str());
151 }
152 return std::make_pair(static_cast<int64_t>(n), static_cast<int64_t>(d));
153}
154
155static std::pair<GncInt128, int64_t>
156numeric_from_decimal_match(const std::string& integer, const std::string& decimal)
157{
158 auto neg = (!integer.empty() && integer[0] == '-');
159 GncInt128 high((neg && integer.length() > 1) || (!neg && !integer.empty())
160 ? stoll(integer)
161 : 0);
162 GncInt128 low{ decimal.empty() ? 0 : stoll(decimal)};
163 auto exp10 = decimal.length();
164 int64_t d = powten(exp10);
165 GncInt128 n = high * d + (neg ? -low : low);
166 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 n = n / powten(exp10 - max_leg_digits);
171 exp10 -= max_leg_digits;
172 }
173 return std::make_pair(n, d);
174}
175
176static std::pair<GncInt128, GncInt128>
177numeric_from_scientific_match(smatch &m)
178{
179 int exp{m[4].matched ? stoi(m[4].str()) : 0};
180 auto neg_exp{exp < 0};
181 exp = neg_exp ? -exp : exp;
182 if (exp >= max_leg_digits)
183 {
184 std::ostringstream errmsg;
185 errmsg << "Exponent " << m[3].str() << " in match " << m[0].str()
186 << " exceeds range that GnuCash can parse.";
187 throw std::overflow_error(errmsg.str());
188 }
189
190 GncInt128 num, denom;
191 auto mult = powten(exp);
192
193 if (m[1].matched)
194 {
195 denom = neg_exp ? mult : 1;
196 num = neg_exp ? stoll(m[1].str()) : mult * stoll(m[1].str());
197 }
198 else
199 {
200 auto [d_num, d_denom] = numeric_from_decimal_match(m[2].str(), m[3].str());
201
202 if (neg_exp || d_denom > mult)
203 {
204 num = d_num;
205 denom = neg_exp ? d_denom * mult : d_denom / mult;
206 }
207 else
208 {
209 num = d_num * mult / d_denom;
210 denom = 1;
211 }
212 }
213 return std::make_pair(num, denom);
214}
215
216static std::optional<gnc_numeric>
217fast_numeral_rational (const char* str)
218{
219 if (!str || !str[0])
220 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 auto end_ptr{(const char*)memchr (str, '\0', 48)};
226 if (!end_ptr)
227 return {};
228
229 int64_t num, denom{};
230 auto result = std::from_chars (str, end_ptr, num);
231 if (result.ec != std::errc())
232 return {};
233
234 if (result.ptr == end_ptr)
235 return gnc_numeric_create (num, 1);
236
237 if (*result.ptr != '/')
238 return {};
239
240 result = std::from_chars (result.ptr + 1, end_ptr, denom);
241 if (result.ec != std::errc() || result.ptr != end_ptr || denom <= 0)
242 return {};
243
244 return gnc_numeric_create (num, denom);
245}
246
247GncNumeric::GncNumeric(const std::string &str, bool autoround) {
248 static const std::string begin("^[^-.0-9]*");
249 static const std::string end("[^0-9]*$");
250 static const std::string begin_group("(?:");
251 static const std::string end_group(")");
252 static const std::string or_op("|");
253 static const std::string maybe_sign ("(-?)");
254 static const std::string opt_signed_int("(-?[0-9]*)");
255 static const std::string opt_signed_separated_int("(-?[0-9]{1,3})");
256 static const std::string unsigned_int("([0-9]+)");
257 static const std::string eu_separated_int("(?:[[:space:]'.]([0-9]{3}))?");
258 static const std::string en_separated_int("(?:\\,([0-9]{3}))?");
259 static const std::string eu_decimal_part("(?:\\,([0-9]+))?");
260 static const std::string en_decimal_part("(?:\\.([0-9]+))?");
261 static const std::string hex_frag("(0[xX][A-Fa-f0-9]+)");
262 static const std::string slash("[ \\t]*/[ \\t]*");
263 static const std::string whitespace("[ \\t]+");
264 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 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 static const regex numeral(begin + opt_signed_int + end);
271 static const regex hex(begin + hex_frag + end);
272 static const regex numeral_rational(begin + opt_signed_int + slash + unsigned_int + end);
273 static const regex integer_and_fraction(begin + maybe_sign + unsigned_int + whitespace + unsigned_int + slash + unsigned_int + end);
274 static const regex hex_rational(begin + hex_frag + slash + hex_frag + end);
275 static const regex hex_over_num(begin + hex_frag + slash + unsigned_int + end);
276 static const regex num_over_hex(begin + opt_signed_int + slash + hex_frag + end);
277 static const regex decimal(begin + opt_signed_int + "[.,]" + unsigned_int + end);
278 static const u32regex sep_decimal =
279 boost::make_u32regex(begin + begin_group + eu_sep_decimal + or_op + en_sep_decimal + end_group + end);
280 static const regex scientific("(?:(-?[0-9]+[.,]?)|(-?[0-9]*)[.,]([0-9]+))[Ee](-?[0-9]+)");
281 static const regex has_hex_prefix(".*0[xX]$");
282 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 if (str.empty())
288 throw std::invalid_argument(
289 "Can't construct a GncNumeric from an empty string.");
290 if (auto res = fast_numeral_rational (str.c_str()))
291 {
292 m_num = res->num;
293 m_den = res->denom;
294 return;
295 }
296 if (regex_search(str, m, hex_rational))
297 {
298 GncNumeric n(stoll(m[1].str(), nullptr, 16),
299 stoll(m[2].str(), nullptr, 16));
300
301 m_num = n.num();
302 m_den = n.denom();
303 return;
304 }
305 if (regex_search(str, m, hex_over_num))
306 {
307 GncNumeric n(stoll(m[1].str(), nullptr, 16), stoll(m[2].str()));
308 m_num = n.num();
309 m_den = n.denom();
310 return;
311 }
312 if (regex_search(str, m, num_over_hex))
313 {
314 GncNumeric n(stoll(m[1].str()), stoll(m[2].str(), nullptr, 16));
315 m_num = n.num();
316 m_den = n.denom();
317 return;
318 }
319 if (regex_search(str, m, integer_and_fraction))
320 {
321 GncNumeric n(stoll(m[3].str()), stoll(m[4].str()));
322 n += stoll(m[2].str());
323 m_num = m[1].str().empty() ? n.num() : -n.num();
324 m_den = n.denom();
325 return;
326 }
327 if (regex_search(str, m, numeral_rational))
328 {
329 GncNumeric n(stoll(m[1].str()), stoll(m[2].str()));
330 m_num = n.num();
331 m_den = n.denom();
332 return;
333 }
334 if (regex_search(str, m, scientific) && ! regex_match(m.prefix().str(), x, has_hex_prefix))
335 {
336 auto [num, denom] =
337 reduce_number_pair(numeric_from_scientific_match(m),
338 str, autoround);
339 m_num = num;
340 m_den = denom;
341 return;
342 }
343 if (regex_search(str, m, decimal))
344 {
345 std::string integer{m[1].matched ? m[1].str() : ""};
346 std::string decimal{m[2].matched ? m[2].str() : ""};
347 auto [num, denom] = reduce_number_pair(numeric_from_decimal_match(integer, decimal), str, autoround);
348 m_num = num;
349 m_den = denom;
350 return;
351 }
352 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 std::string integer(""), decimal("");
365 for (auto i{1}; i < 6; ++i)
366 if (m[i].matched)
367 integer += m[i].str();
368 if (m[6].matched)
369 decimal += m[6].str();
370 if (integer.empty() && decimal.empty())
371 {
372 for (auto i{7}; i <12; ++i)
373 if (m[i].matched)
374 integer += m[i].str();
375 if (m[12].matched)
376 decimal += m[12].str();
377 }
378 auto [num, denom] =
379 reduce_number_pair(numeric_from_decimal_match(integer, decimal),
380 str, autoround);
381 m_num = num;
382 m_den = denom;
383 return;
384 }
385 if (regex_search(str, m, hex))
386 {
387 GncNumeric n(stoll(m[1].str(), nullptr, 16), INT64_C(1));
388 m_num = n.num();
389 m_den = n.denom();
390 return;
391 }
392 if (regex_search(str, m, numeral))
393 {
394 GncNumeric n(stoll(m[1].str()), INT64_C(1));
395 m_num = n.num();
396 m_den = n.denom();
397 return;
398 }
399 std::ostringstream errmsg;
400 errmsg << "String " << str << " contains no recognizable numeric value.";
401 throw std::invalid_argument(errmsg.str());
402}
403
404GncNumeric::operator gnc_numeric() const noexcept
405{
406 return {m_num, m_den};
407}
408
409GncNumeric::operator double() const noexcept
410{
411 return static_cast<double>(m_num) / static_cast<double>(m_den);
412}
413
415GncNumeric::operator-() const noexcept
416{
417 GncNumeric b(*this);
418 b.m_num = - b.m_num;
419 return b;
420}
421
423GncNumeric::inv() const noexcept
424{
425 if (m_num == 0)
426 return *this;
427 if (m_num < 0)
428 return GncNumeric(-m_den, -m_num);
429 return GncNumeric(m_den, m_num);
430}
431
433GncNumeric::abs() const noexcept
434{
435 if (m_num < 0)
436 return -*this;
437 return *this;
438}
439
441GncNumeric::reduce() const noexcept
442{
443 return static_cast<GncNumeric>(GncRational(*this).reduce());
444}
445
446GncNumeric::round_param
447GncNumeric::prepare_conversion(int64_t new_denom) const
448{
449 if (new_denom == m_den || new_denom == GNC_DENOM_AUTO)
450 return {m_num, m_den, 0};
451 GncRational conversion(new_denom, m_den);
452 auto red_conv = conversion.reduce();
453 GncInt128 old_num(m_num);
454 auto new_num = old_num * red_conv.num();
455 auto rem = new_num % red_conv.denom();
456 new_num /= red_conv.denom();
457 if (new_num.isBig())
458 {
459 GncRational rr(new_num, new_denom);
460 GncNumeric nn(rr);
461 rr = rr.convert<RoundType::truncate>(new_denom);
462 return {static_cast<int64_t>(rr.num()), new_denom, 0};
463 }
464 return {static_cast<int64_t>(new_num),
465 static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
466}
467
468int64_t
469GncNumeric::sigfigs_denom(unsigned figs) const noexcept
470{
471 if (m_num == 0)
472 return 1;
473
474 int64_t num_abs{std::abs(m_num)};
475 bool not_frac = num_abs > m_den;
476 int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
477 unsigned digits{};
478 while (val >= 10)
479 {
480 ++digits;
481 val /= 10;
482 }
483 return not_frac ?
484 powten(digits < figs ? figs - digits - 1 : 0) :
485 powten(figs + digits);
486}
487
488std::string
489GncNumeric::to_string() const noexcept
490{
491 std::ostringstream out;
492 out << *this;
493 return out.str();
494}
495
496bool
498{
499 for (unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
500 {
501 if (m_den == pten[pwr])
502 return true;
503 if (m_den % pten[pwr])
504 return false;
505 }
506 return false;
507}
508
510GncNumeric::to_decimal(unsigned int max_places) const
511{
512 if (max_places > max_leg_digits)
513 max_places = max_leg_digits;
514
515 if (m_num == 0)
516 return GncNumeric();
517
518 if (is_decimal())
519 {
520 if (m_num == 0 || m_den < powten(max_places))
521 return *this; // Nothing to do.
522 /* See if we can reduce m_num to fit in max_places */
523 auto excess = m_den / powten(max_places);
524 if (m_num % excess)
525 {
526 std::ostringstream msg;
527 msg << "GncNumeric " << *this
528 << " could not be represented in " << max_places
529 << " decimal places without rounding.\n";
530 throw std::range_error(msg.str());
531 }
532 return GncNumeric(m_num / excess, powten(max_places));
533 }
534 GncRational rr(*this);
535 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 unsigned int pwr{1};
538 for (; pwr <= max_places && !(rr.denom() % powten(pwr)); ++pwr);
539 auto reduce_to = powten(pwr);
540 GncInt128 rr_num(rr.num()), rr_den(rr.denom());
541 if (rr_den % reduce_to)
542 {
543 auto factor(reduce_to / rr.denom());
544 rr_num *= factor;
545 rr_den *= factor;
546 }
547 while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
548 {
549 rr_num /= 10;
550 rr_den /= 10;
551 }
552 try
553 {
554 /* Construct from the parts to avoid the GncRational constructor's
555 * automatic rounding.
556 */
557 return {static_cast<int64_t>(rr_num), static_cast<int64_t>(rr_den)};
558 }
559 catch (const std::invalid_argument& err)
560 {
561 std::ostringstream msg;
562 msg << "GncNumeric " << *this
563 << " could not be represented as a decimal without rounding.\n";
564 throw std::range_error(msg.str());
565 }
566 catch (const std::overflow_error& err)
567 {
568 std::ostringstream msg;
569 msg << "GncNumeric " << *this
570 << " overflows when attempting to convert it to decimal.\n";
571 throw std::range_error(msg.str());
572 }
573 catch (const std::underflow_error& err)
574 {
575 std::ostringstream msg;
576 msg << "GncNumeric " << *this
577 << " underflows when attempting to convert it to decimal.\n";
578 throw std::range_error(msg.str());
579 }
580}
581
582void
583GncNumeric::operator+=(GncNumeric b)
584{
585 *this = *this + b;
586}
587
588void
589GncNumeric::operator-=(GncNumeric b)
590{
591 *this = *this - b;
592}
593
594void
595GncNumeric::operator*=(GncNumeric b)
596{
597 *this = *this * b;
598}
599
600void
601GncNumeric::operator/=(GncNumeric b)
602{
603 *this = *this / b;
604}
605
606int
607GncNumeric::cmp(GncNumeric b)
608{
609 if (m_den == b.denom())
610 {
611 auto b_num = b.num();
612 return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
613 }
614 GncRational an(*this), bn(b);
615 return an.cmp(bn);
616}
617
619operator+(GncNumeric a, GncNumeric b)
620{
621 if (a.num() == 0)
622 return b;
623 if (b.num() == 0)
624 return a;
625 GncRational ar(a), br(b);
626 auto rr = ar + br;
627 return static_cast<GncNumeric>(rr);
628}
629
631operator-(GncNumeric a, GncNumeric b)
632{
633 return a + (-b);
634}
635
637operator*(GncNumeric a, GncNumeric b)
638{
639 if (a.num() == 0 || b.num() == 0)
640 {
641 GncNumeric retval;
642 return retval;
643 }
644 GncRational ar(a), br(b);
645 auto rr = ar * br;
646 return static_cast<GncNumeric>(rr);
647}
648
650operator/(GncNumeric a, GncNumeric b)
651{
652 if (a.num() == 0)
653 {
654 GncNumeric retval;
655 return retval;
656 }
657 if (b.num() == 0)
658 throw std::underflow_error("Attempt to divide by zero.");
659
660 GncRational ar(a), br(b);
661 auto rr = ar / br;
662 return static_cast<GncNumeric>(rr);
663}
664
665template <typename T, typename I> T
666convert(T num, I new_denom, int how)
667{
668 auto rtype = static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK);
669 unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
670
671 auto dtype = static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK);
672 bool sigfigs = dtype == DenomType::sigfigs;
673 if (dtype == DenomType::reduce)
674 num = num.reduce();
675
676 switch (rtype)
677 {
678 case RoundType::floor:
679 if (sigfigs)
680 return num.template convert_sigfigs<RoundType::floor>(figs);
681 else
682 return num.template convert<RoundType::floor>(new_denom);
683 case RoundType::ceiling:
684 if (sigfigs)
685 return num.template convert_sigfigs<RoundType::ceiling>(figs);
686 else
687 return num.template convert<RoundType::ceiling>(new_denom);
688 case RoundType::truncate:
689 if (sigfigs)
690 return num.template convert_sigfigs<RoundType::truncate>(figs);
691 else
692 return num.template convert<RoundType::truncate>(new_denom);
693 case RoundType::promote:
694 if (sigfigs)
695 return num.template convert_sigfigs<RoundType::promote>(figs);
696 else
697 return num.template convert<RoundType::promote>(new_denom);
698 case RoundType::half_down:
699 if (sigfigs)
700 return num.template convert_sigfigs<RoundType::half_down>(figs);
701 else
702 return num.template convert<RoundType::half_down>(new_denom);
703 case RoundType::half_up:
704 if (sigfigs)
705 return num.template convert_sigfigs<RoundType::half_up>(figs);
706 else
707 return num.template convert<RoundType::half_up>(new_denom);
708 case RoundType::bankers:
709 if (sigfigs)
710 return num.template convert_sigfigs<RoundType::bankers>(figs);
711 else
712 return num.template convert<RoundType::bankers>(new_denom);
713 case RoundType::never:
714 if (sigfigs)
715 return num.template convert_sigfigs<RoundType::never>(figs);
716 else
717 return num.template convert<RoundType::never>(new_denom);
718 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 if (sigfigs)
725 return num.template convert_sigfigs<RoundType::truncate>(figs);
726 else
727 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 */
737gnc_numeric_check(gnc_numeric in)
738{
739 if (G_LIKELY(in.denom != 0))
740 {
741 return GNC_ERROR_OK;
742 }
743 else if (in.num)
744 {
745 if ((0 < in.num) || (-4 > in.num))
746 {
747 in.num = (gint64) GNC_ERROR_OVERFLOW;
748 }
749 return (GNCNumericErrorCode) in.num;
750 }
751 else
752 {
753 return GNC_ERROR_ARG;
754 }
755}
756
757
758/* *******************************************************************
759 * gnc_numeric_zero_p
760 ********************************************************************/
761
762gboolean
763gnc_numeric_zero_p(gnc_numeric a)
764{
765 if (gnc_numeric_check(a))
766 {
767 return 0;
768 }
769 else
770 {
771 if ((a.num == 0) && (a.denom != 0))
772 {
773 return 1;
774 }
775 else
776 {
777 return 0;
778 }
779 }
780}
781
782/* *******************************************************************
783 * gnc_numeric_negative_p
784 ********************************************************************/
785
786gboolean
788{
789 if (gnc_numeric_check(a))
790 {
791 return 0;
792 }
793 else
794 {
795 if ((a.num < 0) && (a.denom != 0))
796 {
797 return 1;
798 }
799 else
800 {
801 return 0;
802 }
803 }
804}
805
806/* *******************************************************************
807 * gnc_numeric_positive_p
808 ********************************************************************/
809
810gboolean
812{
813 if (gnc_numeric_check(a))
814 {
815 return 0;
816 }
817 else
818 {
819 if ((a.num > 0) && (a.denom != 0))
820 {
821 return 1;
822 }
823 else
824 {
825 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
836int
837gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
838{
840 {
841 return 0;
842 }
843
844 if (a.denom == b.denom)
845 {
846 if (a.num == b.num) return 0;
847 if (a.num > b.num) return 1;
848 return -1;
849 }
850
851 GncNumeric an (a), bn (b);
852
853 return an.cmp(bn);
854}
855
856
857/* *******************************************************************
858 * gnc_numeric_eq
859 ********************************************************************/
860
861gboolean
862gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
863{
864 return ((a.num == b.num) && (a.denom == b.denom));
865}
866
867
868/* *******************************************************************
869 * gnc_numeric_equal
870 ********************************************************************/
871
872gboolean
873gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
874{
875 if (gnc_numeric_check(a))
876 {
877 /* a is not a valid number, check b */
878 if (gnc_numeric_check(b))
879 /* Both invalid, consider them equal */
880 return TRUE;
881 else
882 /* a invalid, b valid */
883 return FALSE;
884 }
885 if (gnc_numeric_check(b))
886 /* a valid, b invalid */
887 return FALSE;
888
889 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
899int
900gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
901 gint how)
902{
903 gnc_numeric aconv, bconv;
904
905 aconv = gnc_numeric_convert(a, denom, how);
906 bconv = gnc_numeric_convert(b, denom, how);
907
908 return(gnc_numeric_equal(aconv, bconv));
909}
910
911static int64_t
912denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
913{
914 if (denom == GNC_DENOM_AUTO &&
915 (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
916 {
917 GncInt128 ad(a.denom), bd(b.denom);
918 denom = static_cast<int64_t>(ad.lcm(bd));
919 }
920 return denom;
921}
922
923/* *******************************************************************
924 * gnc_numeric_add
925 ********************************************************************/
926
927gnc_numeric
928gnc_numeric_add(gnc_numeric a, gnc_numeric b,
929 gint64 denom, gint how)
930{
932 {
934 }
935 try
936 {
937 denom = denom_lcd(a, b, denom, how);
938 if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
939 {
940 GncNumeric an (a), bn (b);
941 GncNumeric sum = an + bn;
942 return static_cast<gnc_numeric>(convert(sum, denom, how));
943 }
944 GncRational ar(a), br(b);
945 auto sum = ar + br;
946 if (denom == GNC_DENOM_AUTO &&
948 return static_cast<gnc_numeric>(sum.round_to_numeric());
949 sum = convert(sum, denom, how);
950 if (sum.is_big() || !sum.valid())
952 return static_cast<gnc_numeric>(sum);
953 }
954 catch (const std::overflow_error& err)
955 {
956 PWARN("%s", err.what());
958 }
959 catch (const std::invalid_argument& err)
960 {
961 PWARN("%s", err.what());
963 }
964 catch (const std::underflow_error& err)
965 {
966 PWARN("%s", err.what());
968 }
969 catch (const std::domain_error& err)
970 {
971 PWARN("%s", err.what());
973 }
974}
975
976/* *******************************************************************
977 * gnc_numeric_sub
978 ********************************************************************/
979
980gnc_numeric
981gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
982 gint64 denom, gint how)
983{
985 {
987 }
988 try
989 {
990 denom = denom_lcd(a, b, denom, how);
991 if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
992 {
993 GncNumeric an (a), bn (b);
994 auto sum = an - bn;
995 return static_cast<gnc_numeric>(convert(sum, denom, how));
996 }
997 GncRational ar(a), br(b);
998 auto sum = ar - br;
999 if (denom == GNC_DENOM_AUTO &&
1001 return static_cast<gnc_numeric>(sum.round_to_numeric());
1002 sum = convert(sum, denom, how);
1003 if (sum.is_big() || !sum.valid())
1005 return static_cast<gnc_numeric>(sum);
1006 }
1007 catch (const std::overflow_error& err)
1008 {
1009 PWARN("%s", err.what());
1011 }
1012 catch (const std::invalid_argument& err)
1013 {
1014 PWARN("%s", err.what());
1016 }
1017 catch (const std::underflow_error& err)
1018 {
1019 PWARN("%s", err.what());
1021 }
1022 catch (const std::domain_error& err)
1023 {
1024 PWARN("%s", err.what());
1026 }
1027}
1028
1029/* *******************************************************************
1030 * gnc_numeric_mul
1031 ********************************************************************/
1032
1033gnc_numeric
1034gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
1035 gint64 denom, gint how)
1036{
1038 {
1040 }
1041
1042 try
1043 {
1044 denom = denom_lcd(a, b, denom, how);
1045 if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
1046 {
1047 GncNumeric an (a), bn (b);
1048 auto prod = an * bn;
1049 return static_cast<gnc_numeric>(convert(prod, denom, how));
1050 }
1051 GncRational ar(a), br(b);
1052 auto prod = ar * br;
1053 if (denom == GNC_DENOM_AUTO &&
1055 return static_cast<gnc_numeric>(prod.round_to_numeric());
1056 prod = convert(prod, denom, how);
1057 if (prod.is_big() || !prod.valid())
1059 return static_cast<gnc_numeric>(prod);
1060 }
1061 catch (const std::overflow_error& err)
1062 {
1063 PWARN("%s", err.what());
1065 }
1066 catch (const std::invalid_argument& err)
1067 {
1068 PWARN("%s", err.what());
1070 }
1071 catch (const std::underflow_error& err)
1072 {
1073 PWARN("%s", err.what());
1075 }
1076 catch (const std::domain_error& err)
1077 {
1078 PWARN("%s", err.what());
1080 }
1081}
1082
1083
1084/* *******************************************************************
1085 * gnc_numeric_div
1086 ********************************************************************/
1087
1088gnc_numeric
1089gnc_numeric_div(gnc_numeric a, gnc_numeric b,
1090 gint64 denom, gint how)
1091{
1093 {
1095 }
1096 try
1097 {
1098 denom = denom_lcd(a, b, denom, how);
1099 if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
1100 {
1101 GncNumeric an (a), bn (b);
1102 auto quot = an / bn;
1103 return static_cast<gnc_numeric>(convert(quot, denom, how));
1104 }
1105 GncRational ar(a), br(b);
1106 auto quot = ar / br;
1107 if (denom == GNC_DENOM_AUTO &&
1109 return static_cast<gnc_numeric>(quot.round_to_numeric());
1110 quot = static_cast<gnc_numeric>(convert(quot, denom, how));
1111 if (quot.is_big() || !quot.valid())
1113 return static_cast<gnc_numeric>(quot);
1114 }
1115 catch (const std::overflow_error& err)
1116 {
1117 PWARN("%s", err.what());
1119 }
1120 catch (const std::invalid_argument& err)
1121 {
1122 PWARN("%s", err.what());
1124 }
1125 catch (const std::underflow_error& err) //Divide by zero
1126 {
1127 PWARN("%s", err.what());
1129 }
1130 catch (const std::domain_error& err)
1131 {
1132 PWARN("%s", err.what());
1134 }
1135}
1136
1137/* *******************************************************************
1138 * gnc_numeric_neg
1139 * negate the argument
1140 ********************************************************************/
1141
1142gnc_numeric
1143gnc_numeric_neg(gnc_numeric a)
1144{
1145 if (gnc_numeric_check(a))
1146 {
1148 }
1149 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
1157gnc_numeric
1158gnc_numeric_abs(gnc_numeric a)
1159{
1160 if (gnc_numeric_check(a))
1161 {
1163 }
1164 return gnc_numeric_create(ABS(a.num), a.denom);
1165}
1166
1167
1168/* *******************************************************************
1169 * gnc_numeric_convert
1170 ********************************************************************/
1171
1172gnc_numeric
1173gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
1174{
1175 if (gnc_numeric_check(in))
1176 return in;
1177 try
1178 {
1179 return convert(GncNumeric(in), denom, how);
1180 }
1181 catch (const std::invalid_argument& err)
1182 {
1184 }
1185 catch (const std::overflow_error& err)
1186 {
1188 }
1189 catch (const std::underflow_error& err)
1190 {
1192 }
1193 catch (const std::domain_error& err)
1194 {
1196 }
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
1206gnc_numeric
1207gnc_numeric_reduce(gnc_numeric in)
1208{
1209 if (gnc_numeric_check(in))
1210 {
1212 }
1213
1214 if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
1215 return in;
1216 try
1217 {
1218 GncNumeric an (in);
1219 return static_cast<gnc_numeric>(an.reduce());
1220 }
1221 catch (const std::overflow_error& err)
1222 {
1223 PWARN("%s", err.what());
1225 }
1226 catch (const std::invalid_argument& err)
1227 {
1228 PWARN("%s", err.what());
1230 }
1231 catch (const std::underflow_error& err)
1232 {
1233 PWARN("%s", err.what());
1235 }
1236 catch (const std::domain_error& err)
1237 {
1238 PWARN("%s", err.what());
1240 }
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
1253gboolean
1254gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
1255{
1256 int max_places = max_decimal_places == NULL ? max_leg_digits :
1257 *max_decimal_places;
1258 if (a->num == 0) return TRUE;
1259 try
1260 {
1261 GncNumeric an (*a);
1262 auto bn = an.to_decimal(max_places);
1263 *a = static_cast<gnc_numeric>(bn);
1264 return TRUE;
1265 }
1266 catch (const std::exception& err)
1267 {
1268 PINFO ("%s", err.what());
1269 return FALSE;
1270 }
1271}
1272
1273
1274gnc_numeric
1275gnc_numeric_invert(gnc_numeric num)
1276{
1277 if (num.num == 0)
1278 return gnc_numeric_zero();
1279 try
1280 {
1281 return static_cast<gnc_numeric>(GncNumeric(num).inv());
1282 }
1283 catch (const std::overflow_error& err)
1284 {
1285 PWARN("%s", err.what());
1287 }
1288 catch (const std::invalid_argument& err)
1289 {
1290 PWARN("%s", err.what());
1292 }
1293 catch (const std::underflow_error& err)
1294 {
1295 PWARN("%s", err.what());
1297 }
1298 catch (const std::domain_error& err)
1299 {
1300 PWARN("%s", err.what());
1302 }
1303}
1304
1305/* *******************************************************************
1306 * double_to_gnc_numeric
1307 ********************************************************************/
1308
1309#ifdef _MSC_VER
1310# define rint /* */
1311#endif
1312gnc_numeric
1313double_to_gnc_numeric(double in, gint64 denom, gint how)
1314{
1315 try
1316 {
1317 GncNumeric an(in);
1318 return convert(an, denom, how);
1319 }
1320 catch (const std::overflow_error& err)
1321 {
1322 PWARN("%s", err.what());
1324 }
1325 catch (const std::invalid_argument& err)
1326 {
1327 PWARN("%s", err.what());
1329 }
1330 catch (const std::underflow_error& err)
1331 {
1332 PWARN("%s", err.what());
1334 }
1335 catch (const std::domain_error& err)
1336 {
1337 PWARN("%s", err.what());
1339 }
1340}
1341
1342/* *******************************************************************
1343 * gnc_numeric_to_double
1344 ********************************************************************/
1345
1346double
1348{
1349 if (in.denom > 0)
1350 {
1351 return (double)in.num / (double)in.denom;
1352 }
1353 else
1354 {
1355 return (double)(in.num * -in.denom);
1356 }
1357}
1358
1359/* *******************************************************************
1360 * gnc_numeric_error
1361 ********************************************************************/
1362
1363gnc_numeric
1365{
1366 return gnc_numeric_create(error_code, 0LL);
1367}
1368
1369
1370
1371/* *******************************************************************
1372 * gnc_numeric text IO
1373 ********************************************************************/
1374
1375gchar *
1377{
1378 char *result;
1379 int64_t tmpnum = n.num;
1380 int64_t tmpdenom = n.denom;
1381
1382 result = g_strdup_printf("%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
1383
1384 return result;
1385}
1386
1387gchar *
1389{
1390 static char buff[1000];
1391 static char *p = buff;
1392 static const size_t size = 50;
1393 int64_t tmpnum = n.num;
1394 int64_t tmpdenom = n.denom;
1395
1396 p += size;
1397 if ((size_t)(p - buff) > (sizeof(buff) - size))
1398 p = buff;
1399
1400 snprintf(p, size, "%" PRId64 "/%" PRId64, tmpnum, tmpdenom);
1401
1402 return p;
1403}
1404
1405gnc_numeric
1406gnc_numeric_from_string (const gchar* str)
1407{
1408 if (!str)
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 if (auto res = fast_numeral_rational (str))
1415 return *res;
1416
1417 try
1418 {
1419 return GncNumeric (str);
1420 }
1421 catch (const std::exception& err)
1422 {
1423 PWARN("%s", err.what());
1425 }
1426}
1427
1428/* *******************************************************************
1429 * GValue handling
1430 ********************************************************************/
1431static gnc_numeric*
1432gnc_numeric_boxed_copy_func( gnc_numeric *in_gnc_numeric )
1433{
1434 if (!in_gnc_numeric)
1435 return nullptr;
1436
1437 /* newvalue will be passed to g_free so we must allocate with g_malloc. */
1438 auto newvalue = static_cast<gnc_numeric*>(g_malloc (sizeof (gnc_numeric)));
1439 *newvalue = *in_gnc_numeric;
1440
1441 return newvalue;
1442}
1443
1444static void
1445gnc_numeric_boxed_free_func( gnc_numeric *in_gnc_numeric )
1446{
1447 g_free( in_gnc_numeric );
1448}
1449
1450G_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
1457static char *
1458gnc_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
1476int
1477main(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
1500std::ostream&
1501operator<<(std::ostream& s, GncNumeric n)
1502{
1503 using boost::locale::conv::utf_to_utf;
1504 std::basic_ostringstream<wchar_t> ss;
1505 ss.imbue(s.getloc());
1506 ss << n;
1507 s << utf_to_utf<char>(ss.str());
1508 return s;
1509}
1510
1512{
1513 switch (error_code)
1514 {
1515 case GNC_ERROR_OK:
1516 return "GNC_ERROR_OK";
1517 case GNC_ERROR_ARG:
1518 return "GNC_ERROR_ARG";
1519 case GNC_ERROR_OVERFLOW:
1520 return "GNC_ERROR_OVERFLOW";
1522 return "GNC_ERROR_DENOM_DIFF";
1524 return "GNC_ERROR_REMAINDER";
1525 default:
1526 return "<unknown>";
1527 }
1528}
1529
1530/* ======================== END OF FILE =================== */
bool isOverflow() const noexcept
bool isBig() const noexcept
bool isNan() const noexcept
The primary numeric class for representing amounts and values.
GncNumeric to_decimal(unsigned int max_places=17) const
Convert the numeric to have a power-of-10 denominator if possible without rounding.
GncNumeric reduce() const noexcept
Return an equivalent fraction with all common factors between the numerator and the denominator remov...
GncNumeric operator-() const noexcept
bool is_decimal() const noexcept
GncNumeric inv() const noexcept
int64_t denom() const noexcept
Accessor for denominator value.
GncNumeric abs() const noexcept
GncNumeric()
Default constructor provides the zero value.
int64_t num() const noexcept
Accessor for numerator value.
std::string to_string() const noexcept
Return a string representation of the GncNumeric.
Rational number class using GncInt128 for the numerator and denominator.
GncInt128 denom() const noexcept
Denominator accessor.
GncInt128 num() const noexcept
Numerator accessor.
GncRational round_to_numeric() const
Round to fit an int64_t, finding the closest possible approximation.
GncRational convert(GncInt128 new_denom) const
Convert a GncRational to use a new denominator.
GncRational reduce() const
Return an equivalent fraction with all common factors between the numerator and the denominator remov...
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
GNCNumericErrorCode
Error codes.
const char * gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
Returns a string representation of the given GNCNumericErrorCode.
gboolean gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
Attempt to convert the denominator to an exact power of ten without rounding.
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
#define GNC_NUMERIC_RND_MASK
bitmasks for HOW flags.
gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and ...
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
int gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Equivalence predicate: Convert both a and b to denom using the specified DENOM and method HOW,...
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gnc_numeric double_to_gnc_numeric(double in, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
Create a gnc_numeric object that signals the error condition noted by error_code, rather than a numbe...
gnc_numeric gnc_numeric_from_string(const gchar *str)
Read a gnc_numeric from str, skipping any leading whitespace.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value.
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
gnc_numeric gnc_numeric_reduce(gnc_numeric in)
Return input after reducing it by Greater Common Factor (GCF) elimination.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_ERROR_REMAINDER
GNC_HOW_RND_NEVER was specified, but the result could not be converted to the desired denominator wit...
@ GNC_ERROR_OVERFLOW
Intermediate result overflow.
@ GNC_ERROR_DENOM_DIFF
GNC_HOW_DENOM_FIXED was specified, but argument denominators differed.
@ GNC_ERROR_OK
No error.
@ GNC_ERROR_ARG
Argument is not a valid number.
@ GNC_HOW_RND_NEVER
Never round at all, and signal an error if there is a fractional result in a computation.
@ GNC_HOW_DENOM_EXACT
Use any denominator which gives an exactly correct ratio of numerator to denominator.