diff options
-rw-r--r-- | python/py_amount.cc | 4 | ||||
-rw-r--r-- | python/py_value.cc | 2 | ||||
-rw-r--r-- | src/amount.cc | 487 | ||||
-rw-r--r-- | src/amount.h | 12 | ||||
-rw-r--r-- | src/balance.h | 4 | ||||
-rw-r--r-- | src/filters.cc | 8 | ||||
-rw-r--r-- | src/gnucash.cc | 4 | ||||
-rw-r--r-- | src/value.cc | 4 | ||||
-rw-r--r-- | src/value.h | 2 |
9 files changed, 418 insertions, 109 deletions
diff --git a/python/py_amount.cc b/python/py_amount.cc index f5e45888..2df2e1bb 100644 --- a/python/py_amount.cc +++ b/python/py_amount.cc @@ -41,12 +41,14 @@ namespace ledger { using namespace boost::python; +#ifdef INTEGER_MATH amount_t py_round_0(const amount_t& amount) { return amount.round(); } amount_t py_round_1(const amount_t& amount, amount_t::precision_t prec) { return amount.round(prec); } +#endif double py_to_double_0(amount_t& amount) { return amount.to_double(); @@ -268,8 +270,10 @@ internal precision.") .def("abs", &amount_t::abs) .def("__abs__", &amount_t::abs) +#ifdef INTEGER_MATH .def("round", py_round_0) .def("round", py_round_1) +#endif .def("unround", &amount_t::unround) .def("reduce", &amount_t::reduce) diff --git a/python/py_value.cc b/python/py_value.cc index 4156ee44..48cd0feb 100644 --- a/python/py_value.cc +++ b/python/py_value.cc @@ -195,8 +195,10 @@ void export_value() .def("abs", &value_t::abs) .def("__abs__", &value_t::abs) +#ifdef INTEGER_MATH .def("round", &value_t::round) .def("in_place_round", &value_t::in_place_round) +#endif .def("unround", &value_t::unround) .def("reduce", &value_t::reduce) diff --git a/src/amount.cc b/src/amount.cc index 55a71521..9c41885e 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -44,11 +44,16 @@ bool amount_t::keep_tag = false; bool amount_t::stream_fullstrings = false; -#ifndef THREADSAFE +#if !defined(THREADSAFE) // These global temporaries are pre-initialized for the sake of // efficiency, and are reused over and over again. static mpz_t temp; +static mpfr_t tempf; +#ifdef INTEGER_MATH static mpz_t divisor; +#else +static mpq_t tempq; +#endif #endif struct amount_t::bigint_t : public supports_flags<> @@ -56,31 +61,58 @@ struct amount_t::bigint_t : public supports_flags<> #define BIGINT_BULK_ALLOC 0x01 #define BIGINT_KEEP_PREC 0x02 +#ifdef INTEGER_MATH mpz_t val; - precision_t prec; +#else + mpq_t val; +#endif + precision_t prec; // this is only an estimate uint_least16_t ref; uint_fast32_t index; -#define MPZ(bigint) ((bigint)->val) +#define MP(bigint) ((bigint)->val) bigint_t() : prec(0), ref(1), index(0) { TRACE_CTOR(bigint_t, ""); +#ifdef INTEGER_MATH mpz_init(val); +#else + mpq_init(val); +#endif } +#ifdef INTEGER_MATH bigint_t(mpz_t _val) : prec(0), ref(1), index(0) { TRACE_CTOR(bigint_t, "mpz_t"); mpz_init_set(val, _val); } +#else +#if 0 + bigint_t(mpq_t _val) : prec(0), ref(1), index(0) { + TRACE_CTOR(bigint_t, "mpq_t"); + mpq_init(val, _val); + mpq_set(val, _val); + } +#endif +#endif bigint_t(const bigint_t& other) : supports_flags<>(other.flags() & ~BIGINT_BULK_ALLOC), prec(other.prec), ref(1), index(0) { TRACE_CTOR(bigint_t, "copy"); +#ifdef INTEGER_MATH mpz_init_set(val, other.val); +#else + mpq_init(val); + mpq_set(val, other.val); +#endif } ~bigint_t() { TRACE_DTOR(bigint_t); assert(ref == 0); +#ifdef INTEGER_MATH mpz_clear(val); +#else + mpq_clear(val); +#endif } bool valid() const { @@ -106,7 +138,12 @@ amount_t * one = NULL; void amount_t::initialize() { mpz_init(temp); + mpfr_init(tempf); +#ifdef INTEGER_MATH mpz_init(divisor); +#else + mpq_init(tempq); +#endif one = new amount_t(amount_t(1L).unround()); @@ -128,9 +165,14 @@ void amount_t::initialize() void amount_t::shutdown() { mpz_clear(temp); + mpfr_clear(tempf); +#ifdef INTEGER_MATH mpz_clear(divisor); +#else + mpq_clear(tempq); +#endif - delete one; + checked_delete(one); if (current_pool) { checked_delete(current_pool); @@ -175,6 +217,8 @@ void amount_t::_dup() assert(valid()); } +#ifdef INTEGER_MATH + void amount_t::_resize(precision_t prec) { assert(prec < 256); @@ -186,13 +230,15 @@ void amount_t::_resize(precision_t prec) assert(prec > quantity->prec); mpz_ui_pow_ui(divisor, 10, prec - quantity->prec); - mpz_mul(MPZ(quantity), MPZ(quantity), divisor); + mpz_mul(MP(quantity), MP(quantity), divisor); quantity->prec = prec; assert(valid()); } +#endif // INTEGER_MATH + void amount_t::_clear() { if (quantity) { @@ -223,70 +269,38 @@ void amount_t::_release() } -namespace { - amount_t::precision_t convert_double(mpz_t dest, double val) - { - int decpt, sign; - char * buf = dtoa(val, 0, 0, &decpt, &sign, NULL); - char * result; - int len = std::strlen(buf); - - if (decpt <= len) { - decpt = len - decpt; - result = NULL; - } else { - // There were trailing zeros, which we have to put back on in - // order to convert this buffer into an integer. - - int zeroes = decpt - len; - result = new char[len + zeroes + 1]; - - std::strcpy(result, buf); - int i; - for (i = 0; i < zeroes; i++) - result[len + i] = '0'; - result[len + i] = '\0'; - - decpt = (len - decpt) + zeroes; - } - - if (sign) { - char * newbuf = new char[std::strlen(result ? result : buf) + 2]; - newbuf[0] = '-'; - std::strcpy(&newbuf[1], result ? result : buf); - mpz_set_str(dest, newbuf, 10); - checked_array_delete(newbuf); - } else { - mpz_set_str(dest, result ? result : buf, 10); - } - - if (result) - checked_array_delete(result); - freedtoa(buf); - - return decpt; - } -} - amount_t::amount_t(const double val) : commodity_(NULL) { TRACE_CTOR(amount_t, "const double"); quantity = new bigint_t; - quantity->prec = convert_double(MPZ(quantity), val); +#ifdef INTEGER_MATH + mpfr_set_d(tempf, val, GMP_RNDN); +#else + mpq_set_d(MP(quantity), val); +#endif + quantity->prec = extend_by_digits; // an approximation } amount_t::amount_t(const unsigned long val) : commodity_(NULL) { TRACE_CTOR(amount_t, "const unsigned long"); quantity = new bigint_t; - mpz_set_ui(MPZ(quantity), val); +#ifdef INTEGER_MATH + mpz_set_ui(MP(quantity), val); +#else + mpq_set_ui(MP(quantity), val, 1); +#endif } amount_t::amount_t(const long val) : commodity_(NULL) { TRACE_CTOR(amount_t, "const long"); quantity = new bigint_t; - mpz_set_si(MPZ(quantity), val); +#ifdef INTEGER_MATH + mpz_set_si(MP(quantity), val); +#else + mpq_set_si(MP(quantity), val, 1); +#endif } @@ -321,19 +335,23 @@ int amount_t::compare(const amount_t& amt) const "Cannot compare amounts with different commodities: " << commodity().symbol() << " and " << amt.commodity().symbol()); +#ifdef INTEGER_MATH if (quantity->prec == amt.quantity->prec) { - return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)); + return mpz_cmp(MP(quantity), MP(amt.quantity)); } else if (quantity->prec < amt.quantity->prec) { amount_t t(*this); t._resize(amt.quantity->prec); - return mpz_cmp(MPZ(t.quantity), MPZ(amt.quantity)); + return mpz_cmp(MP(t.quantity), MP(amt.quantity)); } else { amount_t t = amt; t._resize(quantity->prec); - return mpz_cmp(MPZ(quantity), MPZ(t.quantity)); + return mpz_cmp(MP(quantity), MP(t.quantity)); } +#else + return mpq_cmp(MP(quantity), MP(amt.quantity)); +#endif } @@ -359,18 +377,25 @@ amount_t& amount_t::operator+=(const amount_t& amt) _dup(); +#ifdef INTEGER_MATH if (quantity->prec == amt.quantity->prec) { - mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); + mpz_add(MP(quantity), MP(quantity), MP(amt.quantity)); } else if (quantity->prec < amt.quantity->prec) { _resize(amt.quantity->prec); - mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); + mpz_add(MP(quantity), MP(quantity), MP(amt.quantity)); } else { amount_t t = amt; t._resize(quantity->prec); - mpz_add(MPZ(quantity), MPZ(quantity), MPZ(t.quantity)); + mpz_add(MP(quantity), MP(quantity), MP(t.quantity)); } +#else + mpq_add(MP(quantity), MP(quantity), MP(amt.quantity)); + + if (quantity->prec < amt.quantity->prec) + quantity->prec = amt.quantity->prec; +#endif return *this; } @@ -397,22 +422,31 @@ amount_t& amount_t::operator-=(const amount_t& amt) _dup(); +#ifdef INTEGER_MATH if (quantity->prec == amt.quantity->prec) { - mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); + mpz_sub(MP(quantity), MP(quantity), MP(amt.quantity)); } else if (quantity->prec < amt.quantity->prec) { _resize(amt.quantity->prec); - mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); + mpz_sub(MP(quantity), MP(quantity), MP(amt.quantity)); } else { amount_t t = amt; t._resize(quantity->prec); - mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(t.quantity)); + mpz_sub(MP(quantity), MP(quantity), MP(t.quantity)); } +#else + mpq_sub(MP(quantity), MP(quantity), MP(amt.quantity)); + + if (quantity->prec < amt.quantity->prec) + quantity->prec = amt.quantity->prec; +#endif return *this; } +#ifdef INTEGER_MATH + namespace { void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec) { @@ -461,6 +495,8 @@ namespace { } } +#endif // INTEGER_MATH + amount_t& amount_t::operator*=(const amount_t& amt) { assert(amt.valid()); @@ -476,17 +512,23 @@ amount_t& amount_t::operator*=(const amount_t& amt) _dup(); - mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); +#ifdef INTEGER_MATH + mpz_mul(MP(quantity), MP(quantity), MP(amt.quantity)); +#else + mpq_mul(MP(quantity), MP(quantity), MP(amt.quantity)); +#endif quantity->prec += amt.quantity->prec; if (! has_commodity()) commodity_ = amt.commodity_; - if (has_commodity() && ! (quantity->has_flags(BIGINT_KEEP_PREC))) { + if (has_commodity() && ! keep_precision()) { precision_t comm_prec = commodity().precision(); if (quantity->prec > comm_prec + extend_by_digits) { - mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, +#ifdef INTEGER_MATH + mpz_round(MP(quantity), MP(quantity), quantity->prec, comm_prec + extend_by_digits); +#endif // INTEGER_MATH quantity->prec = comm_prec + extend_by_digits; } } @@ -515,14 +557,19 @@ amount_t& amount_t::operator/=(const amount_t& amt) // Increase the value's precision, to capture fractional parts after // the divide. Round up in the last position. +#ifdef INTEGER_MATH mpz_ui_pow_ui(divisor, 10, (2 * amt.quantity->prec) + quantity->prec + extend_by_digits + 1U); - mpz_mul(MPZ(quantity), MPZ(quantity), divisor); - mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); + mpz_mul(MP(quantity), MP(quantity), divisor); + mpz_tdiv_q(MP(quantity), MP(quantity), MP(amt.quantity)); quantity->prec += amt.quantity->prec + quantity->prec + extend_by_digits + 1U; - mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, quantity->prec - 1); + mpz_round(MP(quantity), MP(quantity), quantity->prec, quantity->prec - 1); quantity->prec -= 1; +#else + mpq_div(MP(quantity), MP(quantity), MP(amt.quantity)); + quantity->prec += amt.quantity->prec + quantity->prec + extend_by_digits; +#endif if (! has_commodity()) commodity_ = amt.commodity_; @@ -532,11 +579,13 @@ amount_t& amount_t::operator/=(const amount_t& amt) // times), then round the number to within the commodity's precision // plus six places. - if (has_commodity() && ! (quantity->has_flags(BIGINT_KEEP_PREC))) { + if (has_commodity() && ! keep_precision()) { precision_t comm_prec = commodity().precision(); if (quantity->prec > comm_prec + extend_by_digits) { - mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, +#ifdef INTEGER_MATH + mpz_round(MP(quantity), MP(quantity), quantity->prec, comm_prec + extend_by_digits); +#endif // INTEGER_MATH quantity->prec = comm_prec + extend_by_digits; } } @@ -548,22 +597,68 @@ amount_t& amount_t::operator/=(const amount_t& amt) amount_t::precision_t amount_t::precision() const { if (! quantity) - throw_(amount_error, "Cannot determine precision of an uninitialized amount"); + throw_(amount_error, + "Cannot determine precision of an uninitialized amount"); return quantity->prec; } +bool amount_t::keep_precision() const +{ + if (! quantity) + throw_(amount_error, + "Cannot determine if precision of an uninitialized amount is kept"); + + return quantity->has_flags(BIGINT_KEEP_PREC); +} + +void amount_t::set_keep_precision(const bool keep) const +{ + if (! quantity) + throw_(amount_error, + "Cannot set whether to keep the precision of an uninitialized amount"); + + if (keep) + quantity->add_flags(BIGINT_KEEP_PREC); + else + quantity->drop_flags(BIGINT_KEEP_PREC); +} + +amount_t::precision_t amount_t::display_precision(const bool full_precision) const +{ + if (! quantity) + throw_(amount_error, + "Cannot determine display precision of an uninitialized amount"); + + commodity_t& comm(commodity()); + + if (! comm || full_precision || keep_precision()) + return quantity->prec; + else if (comm.precision() != quantity->prec) + return comm.precision(); + else if (quantity->prec) + return quantity->prec; + + return 0; +} + amount_t& amount_t::in_place_negate() { if (quantity) { _dup(); - mpz_neg(MPZ(quantity), MPZ(quantity)); +#ifdef INTEGER_MATH + mpz_neg(MP(quantity), MP(quantity)); +#else + mpq_neg(MP(quantity), MP(quantity)); +#endif } else { throw_(amount_error, "Cannot negate an uninitialized amount"); } return *this; } +#ifdef INTEGER_MATH + amount_t& amount_t::in_place_round() { if (! quantity) @@ -581,9 +676,9 @@ amount_t& amount_t::in_place_round(precision_t prec) throw_(amount_error, "Cannot round an uninitialized amount"); if (quantity && quantity->prec <= prec) { - if (quantity->has_flags(BIGINT_KEEP_PREC)) { + if (keep_precision()) { _dup(); - quantity->drop_flags(BIGINT_KEEP_PREC); + set_keep_precision(false); } return *this; } @@ -591,26 +686,28 @@ amount_t& amount_t::in_place_round(precision_t prec) DEBUG("amount.round", "Rounding " << *this << " to precision " << prec); _dup(); - mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, prec); + mpz_round(MP(quantity), MP(quantity), quantity->prec, prec); quantity->prec = prec; - quantity->drop_flags(BIGINT_KEEP_PREC); + set_keep_precision(false); DEBUG("amount.round", " result = " << *this); return *this; } +#endif // INTEGER_MATH + amount_t amount_t::unround() const { if (! quantity) throw_(amount_error, "Cannot unround an uninitialized amount"); - else if (quantity->has_flags(BIGINT_KEEP_PREC)) + else if (keep_precision()) return *this; amount_t t(*this); t._dup(); - t.quantity->add_flags(BIGINT_KEEP_PREC); + t.set_keep_precision(true); return t; } @@ -647,7 +744,11 @@ optional<amount_t> amount_t::value(const optional<datetime_t>& moment, if (quantity) { optional<price_point_t> point(commodity().find_price(in_terms_of, moment)); if (point) +#ifdef INTEGER_MATH return (point->price * number()).round(); +#else + return point->price * number(); +#endif } else { throw_(amount_error, "Cannot determine value of an uninitialized amount"); } @@ -660,7 +761,11 @@ int amount_t::sign() const if (! quantity) throw_(amount_error, "Cannot determine sign of an uninitialized amount"); - return mpz_sgn(MPZ(quantity)); +#ifdef INTEGER_MATH + return mpz_sgn(MP(quantity)); +#else + return mpq_sgn(MP(quantity)); +#endif } bool amount_t::is_zero() const @@ -669,11 +774,29 @@ bool amount_t::is_zero() const throw_(amount_error, "Cannot determine if an uninitialized amount is zero"); if (has_commodity()) { - if (quantity->prec <= commodity().precision() || - quantity->has_flags(BIGINT_KEEP_PREC)) + if (quantity->prec <= commodity().precision() || keep_precision()) { return is_realzero(); - else + } else { +#ifdef INTEGER_MATH return round(commodity().precision()).sign() == 0; +#else + char * buf; + + mpfr_set_q(tempf, MP(quantity), GMP_RNDN); + mpfr_asprintf(&buf, "%.*RNf", commodity().precision(), tempf); + + bool all_zeroes = true; + for (const char * p = buf; *p; p++) { + if (*p != '0' || *p != '.') { + all_zeroes = false; + break; + } + } + + mpfr_free_str(buf); + return all_zeroes; +#endif + } } return is_realzero(); } @@ -684,10 +807,11 @@ double amount_t::to_double(bool no_check) const if (! quantity) throw_(amount_error, "Cannot convert an uninitialized amount to a double"); +#ifdef INTEGER_MATH mpz_t remainder; mpz_init(remainder); - mpz_set(temp, MPZ(quantity)); + mpz_set(temp, MP(quantity)); mpz_ui_pow_ui(divisor, 10, quantity->prec); mpz_tdiv_qr(temp, remainder, temp, divisor); @@ -708,6 +832,10 @@ double amount_t::to_double(bool no_check) const throw_(amount_error, "Conversion of amount to_double loses precision"); return value; +#else + mpfr_set_q(tempf, MP(quantity), GMP_RNDN); + return mpfr_get_d(tempf, GMP_RNDN); +#endif } long amount_t::to_long(bool no_check) const @@ -715,10 +843,14 @@ long amount_t::to_long(bool no_check) const if (! quantity) throw_(amount_error, "Cannot convert an uninitialized amount to a long"); - mpz_set(temp, MPZ(quantity)); +#ifdef INTEGER_MATH + mpz_set(temp, MP(quantity)); mpz_ui_pow_ui(divisor, 10, quantity->prec); mpz_tdiv_q(temp, temp, divisor); - +#else + mpfr_set_q(tempf, MP(quantity), GMP_RNDN); + mpfr_get_z(temp, tempf, GMP_RNDN); +#endif long value = mpz_get_si(temp); if (! no_check && *this != value) @@ -945,6 +1077,7 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) } else if (last_comma != string::npos && commodity().has_flags(COMMODITY_STYLE_EUROPEAN)) { + comm_flags |= COMMODITY_STYLE_EUROPEAN; quantity->prec = quant.length() - last_comma - 1; } else if (last_period != string::npos && @@ -967,16 +1100,16 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) // Setup the amount's own flags if (flags.has_flags(PARSE_NO_MIGRATE)) - quantity->add_flags(BIGINT_KEEP_PREC); + set_keep_precision(true); // Now we have the final number. Remove commas and periods, if // necessary. if (last_comma != string::npos || last_period != string::npos) { - int len = quant.length(); - char * buf = new char[len + 1]; - const char * p = quant.c_str(); - char * t = buf; + int len = quant.length(); + scoped_array<char> buf(new char[len + 1]); + const char * p = quant.c_str(); + char * t = buf.get(); while (*p) { if (*p == ',' || *p == '.') @@ -985,10 +1118,20 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) } *t = '\0'; - mpz_set_str(MPZ(quantity), buf, 10); - checked_array_delete(buf); +#ifdef INTEGER_MATH + mpz_set_str(MP(quantity), buf.get(), 10); +#else + mpq_set_str(MP(quantity), buf.get(), 10); + mpz_ui_pow_ui(temp, 10, quantity->prec); + mpq_set_z(tempq, temp); + mpq_div(MP(quantity), MP(quantity), tempq); +#endif } else { - mpz_set_str(MPZ(quantity), quant.c_str(), 10); +#ifdef INTEGER_MATH + mpz_set_str(MP(quantity), quant.c_str(), 10); +#else + mpq_set_str(MP(quantity), quant.c_str(), 10); +#endif } if (negative) @@ -1023,7 +1166,6 @@ void amount_t::parse_conversion(const string& larger_str, smaller.commodity().set_larger(larger); } - void amount_t::print(std::ostream& _out, bool omit_commodity, bool full_precision) const { @@ -1040,6 +1182,11 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, std::ostringstream out; + commodity_t& comm(base.commodity()); + precision_t precision = 0; + +#ifdef INTEGER_MATH + mpz_t quotient; mpz_t rquotient; mpz_t remainder; @@ -1053,17 +1200,14 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, // Ensure the value is rounded to the commodity's precision before // outputting it. NOTE: `rquotient' is used here as a temp variable! - commodity_t& comm(base.commodity()); - precision_t precision = 0; - if (quantity) { - if (! comm || full_precision || base.quantity->has_flags(BIGINT_KEEP_PREC)) { + if (! comm || full_precision || base.keep_precision()) { mpz_ui_pow_ui(divisor, 10, base.quantity->prec); - mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor); + mpz_tdiv_qr(quotient, remainder, MP(base.quantity), divisor); precision = base.quantity->prec; } else if (comm.precision() < base.quantity->prec) { - mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec, + mpz_round(rquotient, MP(base.quantity), base.quantity->prec, comm.precision()); mpz_ui_pow_ui(divisor, 10, comm.precision()); mpz_tdiv_qr(quotient, remainder, rquotient, divisor); @@ -1071,18 +1215,18 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, } else if (comm.precision() > base.quantity->prec) { mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec); - mpz_mul(rquotient, MPZ(base.quantity), divisor); + mpz_mul(rquotient, MP(base.quantity), divisor); mpz_ui_pow_ui(divisor, 10, comm.precision()); mpz_tdiv_qr(quotient, remainder, rquotient, divisor); precision = comm.precision(); } else if (base.quantity->prec) { mpz_ui_pow_ui(divisor, 10, base.quantity->prec); - mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor); + mpz_tdiv_qr(quotient, remainder, MP(base.quantity), divisor); precision = base.quantity->prec; } else { - mpz_set(quotient, MPZ(base.quantity)); + mpz_set(quotient, MP(base.quantity)); mpz_set_ui(remainder, 0); precision = 0; } @@ -1189,6 +1333,65 @@ void amount_t::print(std::ostream& _out, bool omit_commodity, mpz_clear(rquotient); mpz_clear(remainder); +#else // INTEGER_MATH + + char * buf; + mpfr_set_q(tempf, MP(quantity), GMP_RNDN); + mpfr_asprintf(&buf, "%.*RNf", base.display_precision(full_precision), tempf); + DEBUG("amount.print", "mpfr_print = " << buf); + + try { + if (! omit_commodity && ! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { + comm.print(out); + if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) + out << " "; + } + + if (omit_commodity || ! comm.has_flags(COMMODITY_STYLE_THOUSANDS)) { + if (! comm.has_flags(COMMODITY_STYLE_EUROPEAN)) + out << buf; + else + for (const char * p = buf; *p; p++) + if (*p == '.') + out << ','; + else + out << *p; + } else { + // Count the number of integer digits + int integer_digits = 0; + for (const char * p = buf; *p; p++) { + if (*p == '.') + break; + else if (std::isdigit(*p)) + integer_digits++; + } + + for (const char * p = buf; *p; p++) { + if (*p == '.' && comm.has_flags(COMMODITY_STYLE_EUROPEAN)) + out << ','; + else + out << *p; + + if (std::isdigit(*p) && integer_digits > 3 && + --integer_digits % 3 == 0) + out << ','; + } + } + } + catch (...) { + mpfr_free_str(buf); + throw; + } + mpfr_free_str(buf); + + if (! omit_commodity && comm.has_flags(COMMODITY_STYLE_SUFFIXED)) { + if (comm.has_flags(COMMODITY_STYLE_SEPARATED)) + out << " "; + comm.print(out); + } + +#endif // INTEGER_MATH + // If there are any annotations associated with this commodity, // output them now. @@ -1230,18 +1433,43 @@ void amount_t::read(std::istream& in) if (byte < 3) { quantity = new bigint_t; +#ifndef INTEGER_MATH + mpz_t numerator; + mpz_t denominator; +#endif + unsigned short len; in.read(reinterpret_cast<char *>(&len), sizeof(len)); assert(len < 4096); static char buf[4096]; in.read(buf, len); - mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short), +#ifdef INTEGER_MATH + mpz_import(MP(quantity), len / sizeof(short), 1, sizeof(short), + 0, 0, buf); +#else + mpz_init(numerator); + mpz_import(numerator, len / sizeof(short), 1, sizeof(short), 0, 0, buf); + in.read(reinterpret_cast<char *>(&len), sizeof(len)); + assert(len < 4096); + in.read(buf, len); + mpz_init(denominator); + mpz_import(denominator, len / sizeof(short), 1, sizeof(short), + 0, 0, buf); + + mpq_set_num(MP(quantity), numerator); + mpq_set_den(MP(quantity), denominator); +#endif + char negative; in.read(&negative, sizeof(negative)); if (negative) - mpz_neg(MPZ(quantity), MPZ(quantity)); +#ifdef INTEGER_MATH + mpz_neg(MP(quantity), MP(quantity)); +#else + mpq_neg(MP(quantity), MP(quantity)); +#endif in.read(reinterpret_cast<char *>(&quantity->prec), sizeof(quantity->prec)); @@ -1285,16 +1513,40 @@ void amount_t::read(const char *& data, quantity = new bigint_t; } +#ifndef INTEGER_MATH + mpz_t numerator; + mpz_t denominator; +#endif + unsigned short len = *reinterpret_cast<unsigned short *>(const_cast<char *>(data)); data += sizeof(unsigned short); - mpz_import(MPZ(quantity), len / sizeof(short), 1, sizeof(short), +#ifdef INTEGER_MATH + mpz_import(MP(quantity), len / sizeof(short), 1, sizeof(short), + 0, 0, data); +#else + mpz_init(numerator); + mpz_import(numerator, len / sizeof(short), 1, sizeof(short), + 0, 0, data); + + len = *reinterpret_cast<unsigned short *>(const_cast<char *>(data)); + data += sizeof(unsigned short); + mpz_init(denominator); + mpz_import(denominator, len / sizeof(short), 1, sizeof(short), 0, 0, data); + + mpq_set_num(MP(quantity), numerator); + mpq_set_den(MP(quantity), denominator); +#endif data += len; char negative = *data++; if (negative) - mpz_neg(MPZ(quantity), MPZ(quantity)); +#ifdef INTEGER_MATH + mpz_neg(MP(quantity), MP(quantity)); +#else + mpq_neg(MP(quantity), MP(quantity)); +#endif quantity->prec = *reinterpret_cast<precision_t *>(const_cast<char *>(data)); data += sizeof(precision_t); @@ -1344,7 +1596,20 @@ void amount_t::write(std::ostream& out, std::size_t index) const std::size_t size; static char buf[4096]; - mpz_export(buf, &size, 1, sizeof(short), 0, 0, MPZ(quantity)); +#ifdef INTEGER_MATH + mpz_export(buf, &size, 1, sizeof(short), 0, 0, MP(quantity)); +#else + mpz_t numerator; + mpz_t denominator; + + mpz_init(numerator); + mpq_get_num(numerator, MP(quantity)); + mpz_export(buf, &size, 1, sizeof(short), 0, 0, numerator); + + mpz_init(denominator); + mpq_get_den(denominator, MP(quantity)); + mpz_export(buf, &size, 1, sizeof(short), 0, 0, denominator); +#endif unsigned short len = size * sizeof(short); out.write(reinterpret_cast<char *>(&len), sizeof(len)); if (len) { @@ -1352,7 +1617,11 @@ void amount_t::write(std::ostream& out, std::size_t index) const out.write(buf, len); } - byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0; +#ifdef INTEGER_MATH + byte = mpz_sgn(MP(quantity)) < 0 ? 1 : 0; +#else + byte = mpq_sgn(MP(quantity)) < 0 ? 1 : 0; +#endif out.write(&byte, sizeof(byte)); out.write(reinterpret_cast<char *>(&quantity->prec), sizeof(quantity->prec)); diff --git a/src/amount.h b/src/amount.h index ba7d1c07..906aef0b 100644 --- a/src/amount.h +++ b/src/amount.h @@ -57,6 +57,11 @@ namespace ledger { +// If defined, amount.cc uses GMP integers rather than rationals. +// Ledger 3.0 uses rationals, but 2.6 and before used integers, so this +// provides a quick way of testing against past numerical behavior. +//#define INTEGER_MATH 1 + class commodity_t; class annotation_t; class commodity_pool_t; @@ -318,6 +323,9 @@ public: amount.commodity().precision() @endcode */ precision_t precision() const; + bool keep_precision() const; + void set_keep_precision(const bool keep = true) const; + precision_t display_precision(const bool full_precision = false) const; /** Returns the negated value of an amount. @see operator-() @@ -344,6 +352,8 @@ public: return *this; } +#ifdef INTEGER_MATH + /** An amount's internal value to the given precision, or to the commodity's current display precision if no precision value is given. This method changes the internal value of the amount, if @@ -363,6 +373,8 @@ public: } amount_t& in_place_round(precision_t prec); +#endif // INTEGER_MATH + /** Yields an amount whose display precision is never truncated, even though its commodity normally displays only rounded values. */ diff --git a/src/balance.h b/src/balance.h index 8f565e78..eec86576 100644 --- a/src/balance.h +++ b/src/balance.h @@ -311,6 +311,8 @@ public: return temp; } +#ifdef INTEGER_MATH + balance_t round() const { balance_t temp; foreach (const amounts_map::value_type& pair, amounts) @@ -335,6 +337,8 @@ public: return *this; } +#endif // INTEGER_MATH + balance_t unround() const { balance_t temp; foreach (const amounts_map::value_type& pair, amounts) diff --git a/src/filters.cc b/src/filters.cc index bfa85a42..47dec09b 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -367,7 +367,11 @@ void changed_value_xacts::output_diff(const date_t& date) value_t cur_bal; last_xact->xdata().date = date; +#ifdef INTEGER_MATH cur_bal = total_expr.calc(*last_xact).round(); +#else + cur_bal = total_expr.calc(*last_xact); +#endif if (value_t diff = cur_bal - last_balance) { entry_temps.push_back(entry_t()); @@ -390,7 +394,11 @@ void changed_value_xacts::operator()(xact_t& xact) item_handler<xact_t>::operator()(xact); +#ifdef INTEGER_MATH last_balance = total_expr.calc(xact).round(); +#else + last_balance = total_expr.calc(xact); +#endif last_xact = &xact; } diff --git a/src/gnucash.cc b/src/gnucash.cc index a1fa40a3..91217699 100644 --- a/src/gnucash.cc +++ b/src/gnucash.cc @@ -187,7 +187,11 @@ static void endElement(void *, const char *name) if (default_commodity) { curr_quant.set_commodity(*default_commodity); +#ifdef INTEGER_MATH value = curr_quant.round(); +#else + value = curr_quant; +#endif if (curr_value.commodity() == *default_commodity) curr_value = value; diff --git a/src/value.cc b/src/value.cc index b6d4b3f4..b63b8d76 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1400,6 +1400,8 @@ value_t value_t::abs() const return NULL_VALUE; } +#ifdef INTEGER_MATH + value_t value_t::round() const { switch (type()) { @@ -1439,6 +1441,8 @@ void value_t::in_place_round() } } +#endif // INTEGER_MATH + value_t value_t::unround() const { switch (type()) { diff --git a/src/value.h b/src/value.h index 393ca3d3..bb04508e 100644 --- a/src/value.h +++ b/src/value.h @@ -404,8 +404,10 @@ public: } value_t abs() const; +#ifdef INTEGER_MATH value_t round() const; void in_place_round(); +#endif value_t unround() const; value_t reduce() const { |