33 #include <boost/regex.hpp> 34 #include <boost/locale/encoding_utf.hpp> 42 #include "gnc-numeric.hpp" 43 #include "gnc-rational.hpp" 45 static QofLogModule log_module =
"qof";
47 static const uint8_t max_leg_digits{17};
48 static const int64_t pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
49 10000000, 100000000, 1000000000,
50 INT64_C(10000000000), INT64_C(100000000000),
51 INT64_C(1000000000000), INT64_C(10000000000000),
52 INT64_C(100000000000000),
53 INT64_C(10000000000000000),
54 INT64_C(100000000000000000),
55 INT64_C(1000000000000000000)};
56 #define POWTEN_OVERFLOW -5 59 powten (
unsigned int exp)
61 if (exp > max_leg_digits)
70 throw std::underflow_error(
"Operation resulted in NaN.");
72 throw std::overflow_error(
"Operation overflowed a 128-bit int.");
78 m_num =
static_cast<int64_t
>(rr.
num());
79 m_den =
static_cast<int64_t
>(rr.
denom());
84 static uint64_t max_leg_value{INT64_C(1000000000000000000)};
85 if (std::isnan(d) || fabs(d) > max_leg_value)
87 std::ostringstream msg;
88 msg <<
"Unable to construct a GncNumeric from " << d <<
".\n";
89 throw std::invalid_argument(msg.str());
91 constexpr
auto max_num =
static_cast<double>(INT64_MAX);
92 auto logval = log10(fabs(d));
96 den_digits = (max_leg_digits + 1) - static_cast<int>(floor(logval) + 1.0);
98 den_digits = max_leg_digits;
99 den = powten(den_digits);
100 auto num_d =
static_cast<double>(den) * d;
101 while (fabs(num_d) > max_num && den_digits > 1)
103 den = powten(--den_digits);
104 num_d =
static_cast<double>(den) * d;
106 auto num =
static_cast<int64_t
>(floor(num_d));
118 using boost::regex_search;
121 static const std::string numer_frag(
"(-?[0-9]*)");
122 static const std::string denom_frag(
"([0-9]+)");
123 static const std::string hex_frag(
"(0x[a-f0-9]+)");
124 static const std::string slash(
"[ \\t]*/[ \\t]*");
129 static const regex numeral(numer_frag);
130 static const regex hex(hex_frag);
131 static const regex numeral_rational(numer_frag + slash + denom_frag);
132 static const regex hex_rational(hex_frag + slash + hex_frag);
133 static const regex hex_over_num(hex_frag + slash + denom_frag);
134 static const regex num_over_hex(numer_frag + slash + hex_frag);
135 static const regex decimal(numer_frag +
"[.,]" + denom_frag);
142 throw std::invalid_argument(
"Can't construct a GncNumeric from an empty string.");
143 if (regex_search(str, m, hex_rational))
146 stoll(m[2].str(),
nullptr, 16));
151 if (regex_search(str, m, hex_over_num))
159 if (regex_search(str, m, num_over_hex))
162 stoll(m[2].str(),
nullptr, 16));
167 if (regex_search(str, m, numeral_rational))
169 GncNumeric n(stoll(m[1].str()), stoll(m[2].str()));
174 if (regex_search(str, m, decimal))
176 auto neg = (m[1].length() && m[1].str()[0] ==
'-');
177 GncInt128 high((neg && m[1].length() > 1) || (!neg && m[1].length()) ?
178 stoll(m[1].str()) : 0);
180 int64_t d = powten(m[2].str().length());
181 GncInt128 n = high * d + (neg ? -low : low);
183 if (!autoround && n.
isBig())
185 std::ostringstream errmsg;
186 errmsg <<
"Decimal string " << m[1].str() <<
"." << m[2].str()
187 <<
"can't be represented in a GncNumeric without rounding.";
188 throw std::overflow_error(errmsg.str());
190 while (n.
isBig() && d > 0)
197 std::ostringstream errmsg;
198 errmsg <<
"Decimal string " << m[1].str() <<
"." << m[2].str()
199 <<
" can't be represented in a GncNumeric, even after reducing denom to " << d;
200 throw std::overflow_error(errmsg.str());
204 m_den = gncn.
denom();
207 if (regex_search(str, m, hex))
209 GncNumeric n(stoll(m[1].str(),
nullptr, 16),INT64_C(1));
214 if (regex_search(str, m, numeral))
221 std::ostringstream errmsg;
222 errmsg <<
"String " << str <<
" contains no recognizable numeric value.";
223 throw std::invalid_argument(errmsg.str());
226 GncNumeric::operator gnc_numeric() const noexcept
228 return {m_num, m_den};
231 GncNumeric::operator double() const noexcept
233 return static_cast<double>(m_num) / static_cast<double>(m_den);
268 GncNumeric::round_param
269 GncNumeric::prepare_conversion(int64_t new_denom)
const 272 return {m_num, m_den, 0};
274 auto red_conv = conversion.reduce();
276 auto new_num = old_num * red_conv.num();
277 auto rem = new_num % red_conv.denom();
278 new_num /= red_conv.denom();
283 rr = rr.convert<RoundType::truncate>(new_denom);
284 return {
static_cast<int64_t
>(rr.num()), new_denom, 0};
286 return {
static_cast<int64_t
>(new_num),
287 static_cast<int64_t>(red_conv.denom()), static_cast<int64_t>(rem)};
291 GncNumeric::sigfigs_denom(
unsigned figs)
const noexcept
296 int64_t num_abs{std::abs(m_num)};
297 bool not_frac = num_abs > m_den;
298 int64_t val{ not_frac ? num_abs / m_den : m_den / num_abs };
306 powten(digits < figs ? figs - digits - 1 : 0) :
307 powten(figs + digits);
313 std::ostringstream out;
321 for (
unsigned pwr = 0; pwr < max_leg_digits && m_den >= pten[pwr]; ++pwr)
323 if (m_den == pten[pwr])
325 if (m_den % pten[pwr])
334 if (max_places > max_leg_digits)
335 max_places = max_leg_digits;
342 if (m_num == 0 || m_den < powten(max_places))
345 auto excess = m_den / powten(max_places);
348 std::ostringstream msg;
349 msg <<
"GncNumeric " << *
this 350 <<
" could not be represented in " << max_places
351 <<
" decimal places without rounding.\n";
352 throw std::range_error(msg.str());
354 return GncNumeric(m_num / excess, powten(max_places));
357 rr = rr.
convert<RoundType::never>(powten(max_places));
360 for (; pwr <= max_places && !(rr.
denom() % powten(pwr)); ++pwr);
361 auto reduce_to = powten(pwr);
363 if (rr_den % reduce_to)
365 auto factor(reduce_to / rr.
denom());
369 while (!rr_num.isZero() && rr_num > 9 && rr_den > 9 && rr_num % 10 == 0)
379 return {
static_cast<int64_t
>(rr_num), static_cast<int64_t>(rr_den)};
381 catch (
const std::invalid_argument& err)
383 std::ostringstream msg;
384 msg <<
"GncNumeric " << *
this 385 <<
" could not be represented as a decimal without rounding.\n";
386 throw std::range_error(msg.str());
388 catch (
const std::overflow_error& err)
390 std::ostringstream msg;
391 msg <<
"GncNumeric " << *
this 392 <<
" overflows when attempting to convert it to decimal.\n";
393 throw std::range_error(msg.str());
395 catch (
const std::underflow_error& err)
397 std::ostringstream msg;
398 msg <<
"GncNumeric " << *
this 399 <<
" underflows when attempting to convert it to decimal.\n";
400 throw std::range_error(msg.str());
431 if (m_den == b.
denom())
433 auto b_num = b.
num();
434 return m_num < b_num ? -1 : b_num < m_num ? 1 : 0;
461 if (a.num() == 0 || b.
num() == 0)
480 throw std::underflow_error(
"Attempt to divide by zero.");
487 template <
typename T,
typename I> T
488 convert(T num, I new_denom,
int how)
491 unsigned int figs = GNC_HOW_GET_SIGFIGS(how);
493 auto dtype =
static_cast<DenomType
>(how & GNC_NUMERIC_DENOM_MASK);
494 bool sigfigs = dtype == DenomType::sigfigs;
495 if (dtype == DenomType::reduce)
500 case RoundType::floor:
502 return num.template convert_sigfigs<RoundType::floor>(figs);
504 return num.template convert<RoundType::floor>(new_denom);
505 case RoundType::ceiling:
507 return num.template convert_sigfigs<RoundType::ceiling>(figs);
509 return num.template convert<RoundType::ceiling>(new_denom);
510 case RoundType::truncate:
512 return num.template convert_sigfigs<RoundType::truncate>(figs);
514 return num.template convert<RoundType::truncate>(new_denom);
515 case RoundType::promote:
517 return num.template convert_sigfigs<RoundType::promote>(figs);
519 return num.template convert<RoundType::promote>(new_denom);
520 case RoundType::half_down:
522 return num.template convert_sigfigs<RoundType::half_down>(figs);
524 return num.template convert<RoundType::half_down>(new_denom);
525 case RoundType::half_up:
527 return num.template convert_sigfigs<RoundType::half_up>(figs);
529 return num.template convert<RoundType::half_up>(new_denom);
530 case RoundType::bankers:
532 return num.template convert_sigfigs<RoundType::bankers>(figs);
534 return num.template convert<RoundType::bankers>(new_denom);
535 case RoundType::never:
537 return num.template convert_sigfigs<RoundType::never>(figs);
539 return num.template convert<RoundType::never>(new_denom);
547 return num.template convert_sigfigs<RoundType::truncate>(figs);
549 return num.template convert<RoundType::truncate>(new_denom);
561 if (G_LIKELY(in.denom != 0))
567 if ((0 < in.num) || (-4 > in.num))
593 if ((a.num == 0) && (a.denom != 0))
617 if ((a.num < 0) && (a.denom != 0))
641 if ((a.num > 0) && (a.denom != 0))
668 if (a.denom == b.denom)
670 if (a.num == b.num)
return 0;
671 if (a.num > b.num)
return 1;
688 return ((a.num == b.num) && (a.denom == b.denom));
727 gnc_numeric aconv, bconv;
736 denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom,
int how)
742 denom =
static_cast<int64_t
>(ad.lcm(bd));
753 gint64 denom, gint how)
761 denom = denom_lcd(a, b, denom, how);
766 return static_cast<gnc_numeric
>(convert(sum, denom, how));
772 return static_cast<gnc_numeric
>(sum.round_to_numeric());
773 sum = convert(sum, denom, how);
774 if (sum.is_big() || !sum.valid())
776 return static_cast<gnc_numeric
>(sum);
778 catch (
const std::overflow_error& err)
780 PWARN(
"%s", err.what());
783 catch (
const std::invalid_argument& err)
785 PWARN(
"%s", err.what());
788 catch (
const std::underflow_error& err)
790 PWARN(
"%s", err.what());
793 catch (
const std::domain_error& err)
795 PWARN(
"%s", err.what());
806 gint64 denom, gint how)
815 denom = denom_lcd(a, b, denom, how);
820 return static_cast<gnc_numeric
>(convert(sum, denom, how));
826 return static_cast<gnc_numeric
>(sum.round_to_numeric());
827 sum = convert(sum, denom, how);
828 if (sum.is_big() || !sum.valid())
830 return static_cast<gnc_numeric
>(sum);
832 catch (
const std::overflow_error& err)
834 PWARN(
"%s", err.what());
837 catch (
const std::invalid_argument& err)
839 PWARN(
"%s", err.what());
842 catch (
const std::underflow_error& err)
844 PWARN(
"%s", err.what());
847 catch (
const std::domain_error& err)
849 PWARN(
"%s", err.what());
860 gint64 denom, gint how)
869 denom = denom_lcd(a, b, denom, how);
874 return static_cast<gnc_numeric
>(convert(prod, denom, how));
880 return static_cast<gnc_numeric
>(prod.round_to_numeric());
881 prod = convert(prod, denom, how);
882 if (prod.is_big() || !prod.valid())
884 return static_cast<gnc_numeric
>(prod);
886 catch (
const std::overflow_error& err)
888 PWARN(
"%s", err.what());
891 catch (
const std::invalid_argument& err)
893 PWARN(
"%s", err.what());
896 catch (
const std::underflow_error& err)
898 PWARN(
"%s", err.what());
901 catch (
const std::domain_error& err)
903 PWARN(
"%s", err.what());
915 gint64 denom, gint how)
923 denom = denom_lcd(a, b, denom, how);
928 return static_cast<gnc_numeric
>(convert(quot, denom, how));
934 return static_cast<gnc_numeric
>(quot.round_to_numeric());
935 quot =
static_cast<gnc_numeric
>(convert(quot, denom, how));
936 if (quot.is_big() || !quot.valid())
938 return static_cast<gnc_numeric
>(quot);
940 catch (
const std::overflow_error& err)
942 PWARN(
"%s", err.what());
945 catch (
const std::invalid_argument& err)
947 PWARN(
"%s", err.what());
950 catch (
const std::underflow_error& err)
952 PWARN(
"%s", err.what());
955 catch (
const std::domain_error& err)
957 PWARN(
"%s", err.what());
974 return gnc_numeric_create(- a.num, a.denom);
989 return gnc_numeric_create(ABS(a.num), a.denom);
1006 catch (
const std::invalid_argument& err)
1010 catch (
const std::overflow_error& err)
1014 catch (
const std::underflow_error& err)
1018 catch (
const std::domain_error& err)
1044 return static_cast<gnc_numeric
>(an.
reduce());
1046 catch (
const std::overflow_error& err)
1048 PWARN(
"%s", err.what());
1051 catch (
const std::invalid_argument& err)
1053 PWARN(
"%s", err.what());
1056 catch (
const std::underflow_error& err)
1058 PWARN(
"%s", err.what());
1061 catch (
const std::domain_error& err)
1063 PWARN(
"%s", err.what());
1081 int max_places = max_decimal_places == NULL ? max_leg_digits :
1082 *max_decimal_places;
1083 if (a->num == 0)
return TRUE;
1088 *a =
static_cast<gnc_numeric
>(bn);
1091 catch (
const std::exception& err)
1093 PINFO (
"%s", err.what());
1103 return gnc_numeric_zero();
1108 catch (
const std::overflow_error& err)
1110 PWARN(
"%s", err.what());
1113 catch (
const std::invalid_argument& err)
1115 PWARN(
"%s", err.what());
1118 catch (
const std::underflow_error& err)
1120 PWARN(
"%s", err.what());
1123 catch (
const std::domain_error& err)
1125 PWARN(
"%s", err.what());
1143 return convert(an, denom, how);
1145 catch (
const std::overflow_error& err)
1147 PWARN(
"%s", err.what());
1150 catch (
const std::invalid_argument& err)
1152 PWARN(
"%s", err.what());
1155 catch (
const std::underflow_error& err)
1157 PWARN(
"%s", err.what());
1160 catch (
const std::domain_error& err)
1162 PWARN(
"%s", err.what());
1176 return (
double)in.num / (double)in.denom;
1180 return (
double)(in.num * -in.denom);
1191 return gnc_numeric_create(error_code, 0LL);
1204 gint64 tmpnum = n.num;
1205 gint64 tmpdenom = n.denom;
1207 result = g_strdup_printf(
"%" G_GINT64_FORMAT
"/%" G_GINT64_FORMAT, tmpnum, tmpdenom);
1215 static char buff[1000];
1216 static char *p = buff;
1217 gint64 tmpnum = n.num;
1218 gint64 tmpdenom = n.denom;
1221 if (p - buff >= 1000) p = buff;
1223 sprintf(p,
"%" G_GINT64_FORMAT
"/%" G_GINT64_FORMAT, tmpnum, tmpdenom);
1234 *n =
static_cast<gnc_numeric
>(an);
1237 catch (
const std::exception& err)
1239 PWARN(
"%s", err.what());
1248 gnc_numeric_boxed_copy_func( gpointer in_ptr )
1250 auto in_gnc_numeric =
static_cast<gnc_numeric*
>(in_ptr);
1251 if (!in_gnc_numeric)
1255 auto newvalue =
static_cast<gnc_numeric*
>(g_malloc (
sizeof (gnc_numeric)));
1256 *newvalue = *in_gnc_numeric;
1262 gnc_numeric_boxed_free_func( gpointer in_gnc_numeric )
1264 g_free( in_gnc_numeric );
1268 gnc_numeric_get_type(
void )
1270 static GType type = 0;
1274 type = g_boxed_type_register_static(
"gnc_numeric",
1275 gnc_numeric_boxed_copy_func,
1276 gnc_numeric_boxed_free_func );
1285 #ifdef _GNC_NUMERIC_TEST 1288 gnc_numeric_print(gnc_numeric in)
1293 retval = g_strdup_printf(
"<ERROR> [%" G_GINT64_FORMAT
" / %" G_GINT64_FORMAT
"]",
1299 retval = g_strdup_printf(
"[%" G_GINT64_FORMAT
" / %" G_GINT64_FORMAT
"]",
1307 main(
int argc,
char ** argv)
1309 gnc_numeric a = gnc_numeric_create(1, 3);
1310 gnc_numeric b = gnc_numeric_create(1, 4);
1316 printf(
"multiply (EXACT): %s * %s = %s\n",
1317 gnc_numeric_print(a), gnc_numeric_print(b),
1320 printf(
"multiply (REDUCE): %s * %s = %s\n",
1321 gnc_numeric_print(a), gnc_numeric_print(b),
1333 using boost::locale::conv::utf_to_utf;
1334 std::basic_ostringstream<wchar_t> ss;
1335 ss.imbue(s.getloc());
1337 s << utf_to_utf<char>(ss.str());
1346 return "GNC_ERROR_OK";
1348 return "GNC_ERROR_ARG";
1350 return "GNC_ERROR_OVERFLOW";
1352 return "GNC_ERROR_DENOM_DIFF";
1354 return "GNC_ERROR_REMAINDER";
GNC_HOW_RND_NEVER was specified, but the result could not be converted to the desired denominator wit...
Reduce the result value by common factor elimination, using the smallest possible value for the denom...
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
bool isBig() const noexcept
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
GncInt128 denom() const noexcept
Denominator accessor.
gnc_numeric double_to_gnc_numeric(double in, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
#define PINFO(format, args...)
Print an informational note.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
GncRational reduce() const
Return an equivalent fraction with all common factors between the numerator and the denominator remov...
GncNumeric operator-() const noexcept
GNCNumericErrorCode
Error codes.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
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.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Use any denominator which gives an exactly correct ratio of numerator to denominator.
The primary numeric class for representing amounts and values.
Intermediate result overflow.
std::string to_string() const noexcept
Return a string representation of the GncNumeric.
GncRational convert(GncInt128 new_denom) const
Convert a GncRational to use a new denominator.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
GncNumeric abs() const noexcept
gboolean string_to_gnc_numeric(const gchar *str, gnc_numeric *n)
Read a gnc_numeric from str, skipping any leading whitespace.
GncNumeric reduce() const noexcept
Return an equivalent fraction with all common factors between the numerator and the denominator remov...
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gnc_numeric gnc_numeric_reduce(gnc_numeric in)
Return input after reducing it by Greater Common Factor (GCF) elimination.
#define PWARN(format, args...)
Log a warning.
double gnc_numeric_to_double(gnc_numeric in)
Convert numeric to floating-point value.
gnc_numeric gnc_numeric_invert(gnc_numeric num)
Invert a gnc_numeric.
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
Change the denominator of a gnc_numeric value to the specified denominator under standard arguments '...
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Multiply a times b, returning the product.
const char * gnc_numeric_errorCode_to_string(GNCNumericErrorCode error_code)
Returns a string representation of the given GNCNumericErrorCode.
GncNumeric to_decimal(unsigned int max_places=17) const
Convert the numeric to have a power-of-10 denominator if possible without rounding.
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...
bool isNan() const noexcept
Argument is not a valid number.
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...
Rational number class using GncInt128 for the numerator and denominator.
Find the least common multiple of the arguments' denominators and use that as the denominator of the ...
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
GncRational round_to_numeric() const
Round to fit an int64_t, finding the closest possible approximation.
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 ...
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
Never round at all, and signal an error if there is a fractional result in a computation.
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a-b.
GncNumeric()
Default constructor provides the zero value.
GncInt128 num() const noexcept
Numerator accessor.
GNC_HOW_DENOM_FIXED was specified, but argument denominators differed.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric in)
Check for error signal in value.
int64_t num() const noexcept
Accessor for numerator value.
GncNumeric inv() const noexcept
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...
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
#define GNC_NUMERIC_RND_MASK
bitmasks for HOW flags.
bool isOverflow() const noexcept
bool is_decimal() const noexcept
int64_t denom() const noexcept
Accessor for denominator value.