Rationals based math is now passing the unit tests.
This commit is contained in:
parent
c96ab6cb0f
commit
e9ff5caa13
14 changed files with 157 additions and 892 deletions
|
|
@ -41,29 +41,6 @@ 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();
|
||||
}
|
||||
double py_to_double_1(amount_t& amount, bool no_check) {
|
||||
return amount.to_double(no_check);
|
||||
}
|
||||
|
||||
long py_to_long_0(amount_t& amount) {
|
||||
return amount.to_long();
|
||||
}
|
||||
long py_to_long_1(amount_t& amount, bool no_check) {
|
||||
return amount.to_long(no_check);
|
||||
}
|
||||
|
||||
boost::optional<amount_t> py_value_0(const amount_t& amount) {
|
||||
return amount.value();
|
||||
}
|
||||
|
|
@ -169,7 +146,9 @@ void export_amount()
|
|||
make_getter(&amount_t::stream_fullstrings),
|
||||
make_setter(&amount_t::stream_fullstrings))
|
||||
|
||||
#if 0
|
||||
.def(init<double>())
|
||||
#endif
|
||||
.def(init<long>())
|
||||
.def(init<std::string>())
|
||||
|
||||
|
|
@ -185,80 +164,108 @@ internal precision.")
|
|||
.def(self == self)
|
||||
.def(self == long())
|
||||
.def(long() == self)
|
||||
#if 0
|
||||
.def(self == double())
|
||||
.def(double() == self)
|
||||
#endif
|
||||
|
||||
.def(self != self)
|
||||
.def(self != long())
|
||||
.def(long() != self)
|
||||
#if 0
|
||||
.def(self != double())
|
||||
.def(double() != self)
|
||||
#endif
|
||||
|
||||
.def(! self)
|
||||
|
||||
.def(self < self)
|
||||
.def(self < long())
|
||||
.def(long() < self)
|
||||
#if 0
|
||||
.def(self < double())
|
||||
.def(double() < self)
|
||||
#endif
|
||||
|
||||
.def(self <= self)
|
||||
.def(self <= long())
|
||||
.def(long() <= self)
|
||||
#if 0
|
||||
.def(self <= double())
|
||||
.def(double() <= self)
|
||||
#endif
|
||||
|
||||
.def(self > self)
|
||||
.def(self > long())
|
||||
.def(long() > self)
|
||||
#if 0
|
||||
.def(self > double())
|
||||
.def(double() > self)
|
||||
#endif
|
||||
|
||||
.def(self >= self)
|
||||
.def(self >= long())
|
||||
.def(long() >= self)
|
||||
#if 0
|
||||
.def(self >= double())
|
||||
.def(double() >= self)
|
||||
#endif
|
||||
|
||||
.def(self += self)
|
||||
.def(self += long())
|
||||
#if 0
|
||||
.def(self += double())
|
||||
#endif
|
||||
|
||||
.def(self + self)
|
||||
.def(self + long())
|
||||
.def(long() + self)
|
||||
#if 0
|
||||
.def(self + double())
|
||||
.def(double() + self)
|
||||
#endif
|
||||
|
||||
.def(self -= self)
|
||||
.def(self -= long())
|
||||
#if 0
|
||||
.def(self -= double())
|
||||
#endif
|
||||
|
||||
.def(self - self)
|
||||
.def(self - long())
|
||||
.def(long() - self)
|
||||
#if 0
|
||||
.def(self - double())
|
||||
.def(double() - self)
|
||||
#endif
|
||||
|
||||
.def(self *= self)
|
||||
.def(self *= long())
|
||||
#if 0
|
||||
.def(self *= double())
|
||||
#endif
|
||||
|
||||
.def(self * self)
|
||||
.def(self * long())
|
||||
.def(long() * self)
|
||||
#if 0
|
||||
.def(self * double())
|
||||
.def(double() * self)
|
||||
#endif
|
||||
|
||||
.def(self /= self)
|
||||
.def(self /= long())
|
||||
#if 0
|
||||
.def(self /= double())
|
||||
#endif
|
||||
|
||||
.def(self / self)
|
||||
.def(self / long())
|
||||
.def(long() / self)
|
||||
#if 0
|
||||
.def(self / double())
|
||||
.def(double() / self)
|
||||
#endif
|
||||
|
||||
.def("precision", &amount_t::precision)
|
||||
|
||||
|
|
@ -270,11 +277,8 @@ 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("rounded", &amount_t::rounded)
|
||||
.def("unrounded", &amount_t::unrounded)
|
||||
|
||||
.def("reduce", &amount_t::reduce)
|
||||
.def("in_place_reduce", &amount_t::in_place_reduce,
|
||||
|
|
@ -295,18 +299,15 @@ internal precision.")
|
|||
.def("is_realzero", &amount_t::is_realzero)
|
||||
.def("is_null", &amount_t::is_null)
|
||||
|
||||
.def("to_double", py_to_double_0)
|
||||
.def("to_double", py_to_double_1)
|
||||
.def("__float__", py_to_double_0)
|
||||
.def("to_long", py_to_long_0)
|
||||
.def("to_long", py_to_long_1)
|
||||
.def("__int__", py_to_long_0)
|
||||
.def("to_double", &amount_t::to_double)
|
||||
.def("__float__", &amount_t::to_double)
|
||||
.def("to_long", &amount_t::to_long)
|
||||
.def("__int__", &amount_t::to_long)
|
||||
.def("to_string", &amount_t::to_string)
|
||||
.def("__str__", &amount_t::to_string)
|
||||
.def("to_fullstring", &amount_t::to_fullstring)
|
||||
.def("__repr__", &amount_t::to_fullstring)
|
||||
|
||||
.def("fits_in_double", &amount_t::fits_in_double)
|
||||
.def("fits_in_long", &amount_t::fits_in_long)
|
||||
|
||||
.def("quantity_string", &amount_t::quantity_string)
|
||||
|
|
@ -359,7 +360,9 @@ internal precision.")
|
|||
|
||||
register_optional_to_python<amount_t>();
|
||||
|
||||
#if 0
|
||||
implicitly_convertible<double, amount_t>();
|
||||
#endif
|
||||
implicitly_convertible<long, amount_t>();
|
||||
implicitly_convertible<string, amount_t>();
|
||||
|
||||
|
|
|
|||
|
|
@ -195,11 +195,8 @@ 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("rounded", &value_t::rounded)
|
||||
.def("unrounded", &value_t::unrounded)
|
||||
|
||||
.def("reduce", &value_t::reduce)
|
||||
.def("in_place_reduce", &value_t::in_place_reduce)
|
||||
|
|
|
|||
580
src/amount.cc
580
src/amount.cc
|
|
@ -47,26 +47,18 @@ bool amount_t::stream_fullstrings = false;
|
|||
#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 mpz_t temp;
|
||||
static mpq_t tempq;
|
||||
static mpfr_t tempf;
|
||||
#ifdef INTEGER_MATH
|
||||
static mpz_t divisor;
|
||||
#else
|
||||
static mpq_t tempq;
|
||||
static mpfr_t tempfb;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct amount_t::bigint_t : public supports_flags<>
|
||||
{
|
||||
#define BIGINT_BULK_ALLOC 0x01
|
||||
#define BIGINT_KEEP_PREC 0x02
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_t val;
|
||||
#else
|
||||
mpq_t val;
|
||||
#endif
|
||||
precision_t prec;
|
||||
uint_least16_t ref;
|
||||
uint_fast32_t index;
|
||||
|
|
@ -75,31 +67,19 @@ struct amount_t::bigint_t : public supports_flags<>
|
|||
|
||||
bigint_t() : prec(0), ref(1), index(0) {
|
||||
TRACE_CTOR(bigint_t, "");
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_init(val);
|
||||
#else
|
||||
mpq_init(val);
|
||||
#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 {
|
||||
|
|
@ -125,15 +105,11 @@ amount_t * one = NULL;
|
|||
void amount_t::initialize()
|
||||
{
|
||||
mpz_init(temp);
|
||||
mpfr_init(tempf);
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_init(divisor);
|
||||
#else
|
||||
mpq_init(tempq);
|
||||
mpfr_init(tempf);
|
||||
mpfr_init(tempfb);
|
||||
#endif
|
||||
|
||||
one = new amount_t(amount_t(1L).unround());
|
||||
one = new amount_t(amount_t(1L).unrounded());
|
||||
|
||||
if (! current_pool)
|
||||
current_pool = new commodity_pool_t;
|
||||
|
|
@ -153,13 +129,9 @@ void amount_t::initialize()
|
|||
void amount_t::shutdown()
|
||||
{
|
||||
mpz_clear(temp);
|
||||
mpfr_clear(tempf);
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_clear(divisor);
|
||||
#else
|
||||
mpq_clear(tempq);
|
||||
mpfr_clear(tempf);
|
||||
mpfr_clear(tempfb);
|
||||
#endif
|
||||
|
||||
checked_delete(one);
|
||||
|
||||
|
|
@ -206,28 +178,6 @@ void amount_t::_dup()
|
|||
assert(valid());
|
||||
}
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
|
||||
void amount_t::_resize(precision_t prec)
|
||||
{
|
||||
assert(prec < 256);
|
||||
|
||||
if (! quantity || prec == quantity->prec)
|
||||
return;
|
||||
|
||||
_dup();
|
||||
|
||||
assert(prec > quantity->prec);
|
||||
mpz_ui_pow_ui(divisor, 10, prec - quantity->prec);
|
||||
mpz_mul(MP(quantity), MP(quantity), divisor);
|
||||
|
||||
quantity->prec = prec;
|
||||
|
||||
assert(valid());
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
void amount_t::_clear()
|
||||
{
|
||||
if (quantity) {
|
||||
|
|
@ -262,12 +212,7 @@ amount_t::amount_t(const double val) : commodity_(NULL)
|
|||
{
|
||||
TRACE_CTOR(amount_t, "const double");
|
||||
quantity = new bigint_t;
|
||||
#ifdef INTEGER_MATH
|
||||
mpfr_set_d(tempf, val, GMP_RNDN);
|
||||
mpfr_get_z(MP(quantity), tempf);
|
||||
#else
|
||||
mpq_set_d(MP(quantity), val);
|
||||
#endif
|
||||
quantity->prec = extend_by_digits; // an approximation
|
||||
}
|
||||
|
||||
|
|
@ -275,22 +220,14 @@ amount_t::amount_t(const unsigned long val) : commodity_(NULL)
|
|||
{
|
||||
TRACE_CTOR(amount_t, "const unsigned long");
|
||||
quantity = new bigint_t;
|
||||
#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;
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_set_si(MP(quantity), val);
|
||||
#else
|
||||
mpq_set_si(MP(quantity), val, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -325,23 +262,7 @@ 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(MP(quantity), MP(amt.quantity));
|
||||
}
|
||||
else if (quantity->prec < amt.quantity->prec) {
|
||||
amount_t t(*this);
|
||||
t._resize(amt.quantity->prec);
|
||||
return mpz_cmp(MP(t.quantity), MP(amt.quantity));
|
||||
}
|
||||
else {
|
||||
amount_t t = amt;
|
||||
t._resize(quantity->prec);
|
||||
return mpz_cmp(MP(quantity), MP(t.quantity));
|
||||
}
|
||||
#else
|
||||
return mpq_cmp(MP(quantity), MP(amt.quantity));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -367,25 +288,10 @@ amount_t& amount_t::operator+=(const amount_t& amt)
|
|||
|
||||
_dup();
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
if (quantity->prec == amt.quantity->prec) {
|
||||
mpz_add(MP(quantity), MP(quantity), MP(amt.quantity));
|
||||
}
|
||||
else if (quantity->prec < amt.quantity->prec) {
|
||||
_resize(amt.quantity->prec);
|
||||
mpz_add(MP(quantity), MP(quantity), MP(amt.quantity));
|
||||
}
|
||||
else {
|
||||
amount_t t = amt;
|
||||
t._resize(quantity->prec);
|
||||
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;
|
||||
}
|
||||
|
|
@ -412,81 +318,14 @@ amount_t& amount_t::operator-=(const amount_t& amt)
|
|||
|
||||
_dup();
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
if (quantity->prec == amt.quantity->prec) {
|
||||
mpz_sub(MP(quantity), MP(quantity), MP(amt.quantity));
|
||||
}
|
||||
else if (quantity->prec < amt.quantity->prec) {
|
||||
_resize(amt.quantity->prec);
|
||||
mpz_sub(MP(quantity), MP(quantity), MP(amt.quantity));
|
||||
}
|
||||
else {
|
||||
amount_t t = amt;
|
||||
t._resize(quantity->prec);
|
||||
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)
|
||||
{
|
||||
// Round `value', with an encoding precision of `value_prec', to a
|
||||
// rounded value with precision `round_prec'. Result is stored in
|
||||
// `out'.
|
||||
|
||||
assert(value_prec > round_prec);
|
||||
|
||||
mpz_t quotient;
|
||||
mpz_t remainder;
|
||||
|
||||
mpz_init(quotient);
|
||||
mpz_init(remainder);
|
||||
|
||||
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
|
||||
mpz_tdiv_qr(quotient, remainder, value, divisor);
|
||||
mpz_divexact_ui(divisor, divisor, 10);
|
||||
mpz_mul_ui(divisor, divisor, 5);
|
||||
|
||||
if (mpz_sgn(remainder) < 0) {
|
||||
mpz_neg(divisor, divisor);
|
||||
if (mpz_cmp(remainder, divisor) < 0) {
|
||||
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
|
||||
mpz_add(remainder, divisor, remainder);
|
||||
mpz_ui_sub(remainder, 0, remainder);
|
||||
mpz_add(out, value, remainder);
|
||||
} else {
|
||||
mpz_sub(out, value, remainder);
|
||||
}
|
||||
} else {
|
||||
if (mpz_cmp(remainder, divisor) >= 0) {
|
||||
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
|
||||
mpz_sub(remainder, divisor, remainder);
|
||||
mpz_add(out, value, remainder);
|
||||
} else {
|
||||
mpz_sub(out, value, remainder);
|
||||
}
|
||||
}
|
||||
mpz_clear(quotient);
|
||||
mpz_clear(remainder);
|
||||
|
||||
// chop off the rounded bits
|
||||
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
|
||||
mpz_tdiv_q(out, out, divisor);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
amount_t& amount_t::operator*=(const amount_t& amt)
|
||||
{
|
||||
assert(amt.valid());
|
||||
|
|
@ -502,11 +341,7 @@ amount_t& amount_t::operator*=(const amount_t& amt)
|
|||
|
||||
_dup();
|
||||
|
||||
#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())
|
||||
|
|
@ -514,13 +349,8 @@ amount_t& amount_t::operator*=(const amount_t& amt)
|
|||
|
||||
if (has_commodity() && ! keep_precision()) {
|
||||
precision_t comm_prec = commodity().precision();
|
||||
if (quantity->prec > comm_prec + extend_by_digits) {
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_round(MP(quantity), MP(quantity), quantity->prec,
|
||||
comm_prec + extend_by_digits);
|
||||
#endif // INTEGER_MATH
|
||||
if (quantity->prec > comm_prec + extend_by_digits)
|
||||
quantity->prec = comm_prec + extend_by_digits;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
|
@ -547,19 +377,8 @@ 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(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(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_;
|
||||
|
|
@ -571,13 +390,8 @@ amount_t& amount_t::operator/=(const amount_t& amt)
|
|||
|
||||
if (has_commodity() && ! keep_precision()) {
|
||||
precision_t comm_prec = commodity().precision();
|
||||
if (quantity->prec > comm_prec + extend_by_digits) {
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_round(MP(quantity), MP(quantity), quantity->prec,
|
||||
comm_prec + extend_by_digits);
|
||||
#endif // INTEGER_MATH
|
||||
if (quantity->prec > comm_prec + extend_by_digits)
|
||||
quantity->prec = comm_prec + extend_by_digits;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
|
@ -634,59 +448,28 @@ amount_t& amount_t::in_place_negate()
|
|||
{
|
||||
if (quantity) {
|
||||
_dup();
|
||||
#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()
|
||||
amount_t amount_t::rounded() const
|
||||
{
|
||||
if (! quantity)
|
||||
throw_(amount_error, "Cannot round an uninitialized amount");
|
||||
|
||||
if (has_commodity())
|
||||
in_place_round(commodity().precision());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
amount_t& amount_t::in_place_round(precision_t prec)
|
||||
{
|
||||
if (! quantity)
|
||||
throw_(amount_error, "Cannot round an uninitialized amount");
|
||||
|
||||
if (quantity && quantity->prec <= prec) {
|
||||
if (keep_precision()) {
|
||||
_dup();
|
||||
set_keep_precision(false);
|
||||
}
|
||||
throw_(amount_error, "Cannot set rounding for an uninitialized amount");
|
||||
else if (! keep_precision())
|
||||
return *this;
|
||||
}
|
||||
|
||||
DEBUG("amount.round", "Rounding " << *this << " to precision " << prec);
|
||||
amount_t t(*this);
|
||||
t._dup();
|
||||
t.set_keep_precision(false);
|
||||
|
||||
_dup();
|
||||
mpz_round(MP(quantity), MP(quantity), quantity->prec, prec);
|
||||
|
||||
quantity->prec = prec;
|
||||
set_keep_precision(false);
|
||||
|
||||
DEBUG("amount.round", " result = " << *this);
|
||||
|
||||
return *this;
|
||||
return t;
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
amount_t amount_t::unround() const
|
||||
amount_t amount_t::unrounded() const
|
||||
{
|
||||
if (! quantity)
|
||||
throw_(amount_error, "Cannot unround an uninitialized amount");
|
||||
|
|
@ -732,11 +515,7 @@ 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
|
||||
return (point->price * number()).rounded();
|
||||
} else {
|
||||
throw_(amount_error, "Cannot determine value of an uninitialized amount");
|
||||
}
|
||||
|
|
@ -749,15 +528,9 @@ int amount_t::sign() const
|
|||
if (! quantity)
|
||||
throw_(amount_error, "Cannot determine sign of an uninitialized amount");
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
return mpz_sgn(MP(quantity));
|
||||
#else
|
||||
return mpq_sgn(MP(quantity));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef INTEGER_MATH
|
||||
|
||||
namespace {
|
||||
void stream_out_mpq(std::ostream& out, mpq_t quant,
|
||||
amount_t::precision_t prec,
|
||||
|
|
@ -826,8 +599,6 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
bool amount_t::is_zero() const
|
||||
{
|
||||
if (! quantity)
|
||||
|
|
@ -837,9 +608,6 @@ bool amount_t::is_zero() const
|
|||
if (keep_precision() || quantity->prec <= commodity().precision()) {
|
||||
return is_realzero();
|
||||
} else {
|
||||
#ifdef INTEGER_MATH
|
||||
return round(commodity().precision()).sign() == 0;
|
||||
#else
|
||||
std::ostringstream out;
|
||||
stream_out_mpq(out, MP(quantity), commodity().precision());
|
||||
|
||||
|
|
@ -847,80 +615,34 @@ bool amount_t::is_zero() const
|
|||
if (*p != '0' && *p != '.')
|
||||
return false;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return is_realzero();
|
||||
}
|
||||
|
||||
|
||||
double amount_t::to_double(bool no_check) const
|
||||
double amount_t::to_double() 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, MP(quantity));
|
||||
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
||||
mpz_tdiv_qr(temp, remainder, temp, divisor);
|
||||
|
||||
char * quotient_s = mpz_get_str(NULL, 10, temp);
|
||||
char * remainder_s = mpz_get_str(NULL, 10, remainder);
|
||||
|
||||
std::ostringstream num;
|
||||
num << quotient_s << '.' << remainder_s;
|
||||
|
||||
std::free(quotient_s);
|
||||
std::free(remainder_s);
|
||||
|
||||
mpz_clear(remainder);
|
||||
|
||||
double value = lexical_cast<double>(num.str());
|
||||
#else
|
||||
mpfr_set_q(tempf, MP(quantity), GMP_RNDN);
|
||||
double value = mpfr_get_d(tempf, GMP_RNDN);
|
||||
#endif
|
||||
|
||||
if (! no_check && *this != value)
|
||||
throw_(amount_error, "Conversion of amount to_double loses precision");
|
||||
|
||||
return value;
|
||||
return mpfr_get_d(tempf, GMP_RNDN);
|
||||
}
|
||||
|
||||
long amount_t::to_long(bool no_check) const
|
||||
long amount_t::to_long() const
|
||||
{
|
||||
if (! quantity)
|
||||
throw_(amount_error, "Cannot convert an uninitialized amount to a long");
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
mpz_set(temp, MP(quantity));
|
||||
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
||||
mpz_tdiv_q(temp, temp, divisor);
|
||||
long value = mpz_get_si(temp);
|
||||
#else
|
||||
mpfr_set_q(tempf, MP(quantity), GMP_RNDN);
|
||||
long value = mpfr_get_si(tempf, GMP_RNDN);
|
||||
#endif
|
||||
|
||||
if (! no_check && *this != value)
|
||||
throw_(amount_error, "Conversion of amount to_long loses precision");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool amount_t::fits_in_double() const
|
||||
{
|
||||
double value = to_double(true);
|
||||
return *this == amount_t(value);
|
||||
return mpfr_get_si(tempf, GMP_RNDN);
|
||||
}
|
||||
|
||||
bool amount_t::fits_in_long() const
|
||||
{
|
||||
long value = to_long(true);
|
||||
return *this == amount_t(value);
|
||||
mpfr_set_q(tempf, MP(quantity), GMP_RNDN);
|
||||
return mpfr_fits_slong_p(tempf, GMP_RNDN);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1170,9 +892,6 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
|
|||
}
|
||||
*t = '\0';
|
||||
|
||||
#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);
|
||||
|
|
@ -1183,13 +902,8 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags)
|
|||
DEBUG("amount.parse", "Rational parsed = " << buf);
|
||||
std::free(buf);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#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)
|
||||
|
|
@ -1243,156 +957,6 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
|
|||
commodity_t& comm(base.commodity());
|
||||
precision_t precision = 0;
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
|
||||
mpz_t quotient;
|
||||
mpz_t rquotient;
|
||||
mpz_t remainder;
|
||||
|
||||
mpz_init(quotient);
|
||||
mpz_init(rquotient);
|
||||
mpz_init(remainder);
|
||||
|
||||
bool negative = false;
|
||||
|
||||
// Ensure the value is rounded to the commodity's precision before
|
||||
// outputting it. NOTE: `rquotient' is used here as a temp variable!
|
||||
|
||||
if (quantity) {
|
||||
if (! comm || full_precision || base.keep_precision()) {
|
||||
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
|
||||
mpz_tdiv_qr(quotient, remainder, MP(base.quantity), divisor);
|
||||
precision = base.quantity->prec;
|
||||
}
|
||||
else if (comm.precision() < 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);
|
||||
precision = comm.precision();
|
||||
}
|
||||
else if (comm.precision() > base.quantity->prec) {
|
||||
mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
|
||||
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, MP(base.quantity), divisor);
|
||||
precision = base.quantity->prec;
|
||||
}
|
||||
else {
|
||||
mpz_set(quotient, MP(base.quantity));
|
||||
mpz_set_ui(remainder, 0);
|
||||
precision = 0;
|
||||
}
|
||||
|
||||
if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
|
||||
negative = true;
|
||||
|
||||
mpz_abs(quotient, quotient);
|
||||
mpz_abs(remainder, remainder);
|
||||
}
|
||||
mpz_set(rquotient, remainder);
|
||||
}
|
||||
|
||||
if (! omit_commodity && ! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
|
||||
comm.print(out);
|
||||
if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
|
||||
out << " ";
|
||||
}
|
||||
|
||||
if (negative)
|
||||
out << "-";
|
||||
|
||||
if (! quantity || mpz_sgn(quotient) == 0) {
|
||||
out << '0';
|
||||
}
|
||||
else if (omit_commodity || ! comm.has_flags(COMMODITY_STYLE_THOUSANDS)) {
|
||||
char * p = mpz_get_str(NULL, 10, quotient);
|
||||
out << p;
|
||||
std::free(p);
|
||||
}
|
||||
else {
|
||||
std::list<string> strs;
|
||||
char buf[4];
|
||||
|
||||
for (int powers = 0; true; powers += 3) {
|
||||
if (powers > 0) {
|
||||
mpz_ui_pow_ui(divisor, 10, powers);
|
||||
mpz_tdiv_q(temp, quotient, divisor);
|
||||
if (mpz_sgn(temp) == 0)
|
||||
break;
|
||||
mpz_tdiv_r_ui(temp, temp, 1000);
|
||||
} else {
|
||||
mpz_tdiv_r_ui(temp, quotient, 1000);
|
||||
}
|
||||
mpz_get_str(buf, 10, temp);
|
||||
strs.push_back(buf);
|
||||
}
|
||||
|
||||
bool printed = false;
|
||||
|
||||
for (std::list<string>::reverse_iterator i = strs.rbegin();
|
||||
i != strs.rend();
|
||||
i++) {
|
||||
if (printed) {
|
||||
out << (comm.has_flags(COMMODITY_STYLE_EUROPEAN) ? '.' : ',');
|
||||
out.width(3);
|
||||
out.fill('0');
|
||||
}
|
||||
out << *i;
|
||||
|
||||
printed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (quantity && precision) {
|
||||
std::ostringstream final;
|
||||
final.width(precision);
|
||||
final.fill('0');
|
||||
char * p = mpz_get_str(NULL, 10, rquotient);
|
||||
final << p;
|
||||
std::free(p);
|
||||
|
||||
const string& str(final.str());
|
||||
int i, len = str.length();
|
||||
const char * q = str.c_str();
|
||||
for (i = len; i > 0; i--)
|
||||
if (q[i - 1] != '0')
|
||||
break;
|
||||
|
||||
string ender;
|
||||
if (i == len)
|
||||
ender = str;
|
||||
else if (i < comm.precision())
|
||||
ender = string(str, 0, comm.precision());
|
||||
else
|
||||
ender = string(str, 0, i);
|
||||
|
||||
if (! ender.empty()) {
|
||||
if (omit_commodity)
|
||||
out << '.';
|
||||
else
|
||||
out << (comm.has_flags(COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
|
||||
out << ender;
|
||||
}
|
||||
}
|
||||
|
||||
if (! omit_commodity && comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
|
||||
if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
|
||||
out << " ";
|
||||
comm.print(out);
|
||||
}
|
||||
|
||||
mpz_clear(quotient);
|
||||
mpz_clear(rquotient);
|
||||
mpz_clear(remainder);
|
||||
|
||||
#else // INTEGER_MATH
|
||||
|
||||
if (! omit_commodity && ! comm.has_flags(COMMODITY_STYLE_SUFFIXED)) {
|
||||
comm.print(out);
|
||||
if (comm.has_flags(COMMODITY_STYLE_SEPARATED))
|
||||
|
|
@ -1408,8 +972,6 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
|
|||
comm.print(out);
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
// If there are any annotations associated with this commodity,
|
||||
// output them now.
|
||||
|
||||
|
|
@ -1451,43 +1013,27 @@ 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);
|
||||
#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),
|
||||
|
||||
mpz_import(temp, len / sizeof(short), 1, sizeof(short),
|
||||
0, 0, buf);
|
||||
mpq_set_num(MP(quantity), temp);
|
||||
|
||||
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),
|
||||
mpz_import(temp, len / sizeof(short), 1, sizeof(short),
|
||||
0, 0, buf);
|
||||
|
||||
mpq_set_num(MP(quantity), numerator);
|
||||
mpq_set_den(MP(quantity), denominator);
|
||||
#endif
|
||||
mpq_set_den(MP(quantity), temp);
|
||||
|
||||
char negative;
|
||||
in.read(&negative, sizeof(negative));
|
||||
if (negative)
|
||||
#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));
|
||||
|
||||
|
|
@ -1531,40 +1077,28 @@ 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);
|
||||
#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),
|
||||
mpz_init(temp);
|
||||
mpz_import(temp, len / sizeof(short), 1, sizeof(short),
|
||||
0, 0, data);
|
||||
data += len;
|
||||
|
||||
mpq_set_num(MP(quantity), temp);
|
||||
|
||||
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),
|
||||
mpz_init(temp);
|
||||
mpz_import(temp, len / sizeof(short), 1, sizeof(short),
|
||||
0, 0, data);
|
||||
|
||||
mpq_set_num(MP(quantity), numerator);
|
||||
mpq_set_den(MP(quantity), denominator);
|
||||
#endif
|
||||
data += len;
|
||||
|
||||
mpq_set_den(MP(quantity), temp);
|
||||
|
||||
char negative = *data++;
|
||||
if (negative)
|
||||
#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);
|
||||
|
|
@ -1585,6 +1119,20 @@ void amount_t::read(const char *& data,
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void write_bytes(std::ostream& out,
|
||||
const char * buf,
|
||||
const std::size_t size)
|
||||
{
|
||||
unsigned short len = size * sizeof(short);
|
||||
out.write(reinterpret_cast<char *>(&len), sizeof(len));
|
||||
if (len) {
|
||||
assert(len < 4096);
|
||||
out.write(buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void amount_t::write(std::ostream& out, std::size_t index) const
|
||||
{
|
||||
using namespace ledger::binary;
|
||||
|
|
@ -1614,32 +1162,16 @@ void amount_t::write(std::ostream& out, std::size_t index) const
|
|||
|
||||
std::size_t size;
|
||||
static char buf[4096];
|
||||
#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);
|
||||
mpq_get_num(temp, MP(quantity));
|
||||
mpz_export(buf, &size, 1, sizeof(short), 0, 0, temp);
|
||||
write_bytes(out, buf, size);
|
||||
|
||||
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) {
|
||||
assert(len < 4096);
|
||||
out.write(buf, len);
|
||||
}
|
||||
mpq_get_den(temp, MP(quantity));
|
||||
mpz_export(buf, &size, 1, sizeof(short), 0, 0, temp);
|
||||
write_bytes(out, buf, size);
|
||||
|
||||
#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));
|
||||
|
|
|
|||
53
src/amount.h
53
src/amount.h
|
|
@ -57,11 +57,6 @@
|
|||
|
||||
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;
|
||||
|
|
@ -147,9 +142,6 @@ public:
|
|||
protected:
|
||||
void _copy(const amount_t& amt);
|
||||
void _dup();
|
||||
#ifdef INTEGER_MATH
|
||||
void _resize(precision_t prec);
|
||||
#endif
|
||||
void _clear();
|
||||
void _release();
|
||||
|
||||
|
|
@ -354,33 +346,16 @@ 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
|
||||
it's internal precision was greater than the rounding precision.
|
||||
*/
|
||||
amount_t round() const {
|
||||
amount_t temp(*this);
|
||||
temp.in_place_round();
|
||||
return temp;
|
||||
}
|
||||
amount_t& in_place_round();
|
||||
|
||||
amount_t round(precision_t prec) const {
|
||||
amount_t temp(*this);
|
||||
temp.in_place_round(prec);
|
||||
return temp;
|
||||
}
|
||||
amount_t& in_place_round(precision_t prec);
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
/** Yields an amount whose display precision when output is truncated
|
||||
to the display precision of its commodity. This is normally the
|
||||
default state of an amount, but if one has become unrounded, this
|
||||
sets the "keep precision" state back to false.
|
||||
@see set_keep_precision */
|
||||
amount_t rounded() const;
|
||||
|
||||
/** Yields an amount whose display precision is never truncated, even
|
||||
though its commodity normally displays only rounded values.
|
||||
*/
|
||||
amount_t unround() const;
|
||||
though its commodity normally displays only rounded values. */
|
||||
amount_t unrounded() const;
|
||||
|
||||
/** reduces a value to its most basic commodity form, for amounts that
|
||||
utilize "scaling commodities". For example, an amount of \c 1h
|
||||
|
|
@ -487,9 +462,6 @@ public:
|
|||
optional boolean argument is true (the default), an exception is
|
||||
thrown if the conversion would lose information.
|
||||
|
||||
fits_in_double() returns true if to_double() would not lose
|
||||
precision.
|
||||
|
||||
fits_in_long() returns true if to_long() would not lose
|
||||
precision.
|
||||
|
||||
|
|
@ -506,15 +478,14 @@ public:
|
|||
been stripped and the full, internal precision of the amount
|
||||
would be displayed.
|
||||
*/
|
||||
double to_double(bool no_check = false) const;
|
||||
long to_long(bool no_check = false) const;
|
||||
double to_double() const;
|
||||
long to_long() const;
|
||||
bool fits_in_long() const;
|
||||
|
||||
string to_string() const;
|
||||
string to_fullstring() const;
|
||||
string quantity_string() const;
|
||||
|
||||
bool fits_in_double() const;
|
||||
bool fits_in_long() const;
|
||||
|
||||
/*@}*/
|
||||
|
||||
/** @name Commodity methods
|
||||
|
|
|
|||
|
|
@ -311,38 +311,17 @@ public:
|
|||
return temp;
|
||||
}
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
|
||||
balance_t round() const {
|
||||
balance_t rounded() const {
|
||||
balance_t temp;
|
||||
foreach (const amounts_map::value_type& pair, amounts)
|
||||
temp += pair.second.round();
|
||||
temp += pair.second.rounded();
|
||||
return temp;
|
||||
}
|
||||
balance_t& in_place_round() {
|
||||
foreach (amounts_map::value_type& pair, amounts)
|
||||
pair.second.in_place_round();
|
||||
return *this;
|
||||
}
|
||||
|
||||
balance_t round(amount_t::precision_t prec) const {
|
||||
balance_t unrounded() const {
|
||||
balance_t temp;
|
||||
foreach (const amounts_map::value_type& pair, amounts)
|
||||
temp += pair.second.round(prec);
|
||||
return temp;
|
||||
}
|
||||
balance_t& in_place_round(amount_t::precision_t prec) {
|
||||
foreach (amounts_map::value_type& pair, amounts)
|
||||
pair.second.in_place_round(prec);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
balance_t unround() const {
|
||||
balance_t temp;
|
||||
foreach (const amounts_map::value_type& pair, amounts)
|
||||
temp += pair.second.unround();
|
||||
temp += pair.second.unrounded();
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -657,15 +657,13 @@ void annotation_t::parse(std::istream& in)
|
|||
temp.parse(buf, amount_t::PARSE_NO_MIGRATE);
|
||||
temp.in_place_reduce();
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
// Since this price will maintain its own precision, make sure
|
||||
// it is at least as large as the base commodity, since the user
|
||||
// may have only specified {$1} or something similar.
|
||||
|
||||
if (temp.has_commodity() &&
|
||||
temp.precision() > temp.commodity().precision())
|
||||
temp = temp.round(); // no need to retain individual precision
|
||||
#endif
|
||||
temp = temp.rounded(); // no need to retain individual precision
|
||||
|
||||
price = temp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ bool entry_base_t::finalize()
|
|||
add_error_context(entry_context(*this));
|
||||
#endif
|
||||
add_error_context("Unbalanced remainder is: ");
|
||||
add_error_context(value_context(balance.unround()));
|
||||
add_error_context(value_context(balance.unrounded()));
|
||||
throw_(balance_error, "Entry does not balance");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -367,11 +367,7 @@ 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
|
||||
cur_bal = total_expr.calc(*last_xact).rounded();
|
||||
|
||||
if (value_t diff = cur_bal - last_balance) {
|
||||
entry_temps.push_back(entry_t());
|
||||
|
|
@ -394,11 +390,7 @@ 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_balance = total_expr.calc(xact).rounded();
|
||||
last_xact = &xact;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,11 +187,7 @@ 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
|
||||
value = curr_quant.rounded();
|
||||
|
||||
if (curr_value.commodity() == *default_commodity)
|
||||
curr_value = value;
|
||||
|
|
|
|||
38
src/value.cc
38
src/value.cc
|
|
@ -1400,56 +1400,28 @@ value_t value_t::abs() const
|
|||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
#ifdef INTEGER_MATH
|
||||
|
||||
value_t value_t::round() const
|
||||
value_t value_t::rounded() const
|
||||
{
|
||||
switch (type()) {
|
||||
case INTEGER:
|
||||
return *this;
|
||||
case AMOUNT:
|
||||
return as_amount().round();
|
||||
case BALANCE:
|
||||
return as_balance().round();
|
||||
case BALANCE_PAIR:
|
||||
return as_balance_pair().round();
|
||||
return as_amount().rounded();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
throw_(value_error, "Cannot round " << label());
|
||||
throw_(value_error, "Cannot set rounding for " << label());
|
||||
return NULL_VALUE;
|
||||
}
|
||||
|
||||
void value_t::in_place_round()
|
||||
{
|
||||
switch (type()) {
|
||||
case INTEGER:
|
||||
break;
|
||||
case AMOUNT:
|
||||
as_amount_lval().in_place_round();
|
||||
break;
|
||||
case BALANCE:
|
||||
as_balance_lval().in_place_round();
|
||||
break;
|
||||
case BALANCE_PAIR:
|
||||
as_balance_pair_lval().in_place_round();
|
||||
break;
|
||||
default:
|
||||
throw_(value_error, "Cannot round " << label());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
|
||||
value_t value_t::unround() const
|
||||
value_t value_t::unrounded() const
|
||||
{
|
||||
switch (type()) {
|
||||
case INTEGER:
|
||||
return *this;
|
||||
case AMOUNT:
|
||||
return as_amount().unround();
|
||||
return as_amount().unrounded();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -404,11 +404,9 @@ public:
|
|||
}
|
||||
|
||||
value_t abs() const;
|
||||
#ifdef INTEGER_MATH
|
||||
value_t round() const;
|
||||
void in_place_round();
|
||||
#endif
|
||||
value_t unround() const;
|
||||
|
||||
value_t rounded() const;
|
||||
value_t unrounded() const;
|
||||
|
||||
value_t reduce() const {
|
||||
value_t temp(*this);
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ void AmountTestCase::testCommodityConstructors()
|
|||
}
|
||||
|
||||
#ifndef NOT_FOR_PYTHON
|
||||
|
||||
void AmountTestCase::testAssignment()
|
||||
{
|
||||
amount_t x0;
|
||||
|
|
@ -318,6 +319,7 @@ void AmountTestCase::testCommodityAssignment()
|
|||
assertValid(x9);
|
||||
assertValid(x10);
|
||||
}
|
||||
|
||||
#endif // NOT_FOR_PYTHON
|
||||
|
||||
void AmountTestCase::testEquality()
|
||||
|
|
@ -423,8 +425,10 @@ void AmountTestCase::testComparisons()
|
|||
assertTrue(100L > x1);
|
||||
assertTrue(x1 < 100UL);
|
||||
assertTrue(100UL > x1);
|
||||
#ifndef NOT_FOR_PYTHON
|
||||
assertTrue(x1 < 100.0);
|
||||
assertTrue(100.0 > x1);
|
||||
#endif // NOT_FOR_PYTHON
|
||||
|
||||
assertValid(x0);
|
||||
assertValid(x1);
|
||||
|
|
@ -538,7 +542,9 @@ void AmountTestCase::testCommodityAddition()
|
|||
assertThrow(x1 + x4, amount_error);
|
||||
assertThrow(x1 + x5, amount_error);
|
||||
assertThrow(x1 + x6, amount_error);
|
||||
#ifndef NOT_FOR_PYTHON
|
||||
assertThrow(x1 + 123.45, amount_error);
|
||||
#endif // NOT_FOR_PYTHON
|
||||
assertThrow(x1 + 123L, amount_error);
|
||||
|
||||
assertEqual(amount_t("DM 246.90"), x3 + x3);
|
||||
|
|
@ -652,7 +658,9 @@ void AmountTestCase::testCommoditySubtraction()
|
|||
assertThrow(x1 - x4, amount_error);
|
||||
assertThrow(x1 - x5, amount_error);
|
||||
assertThrow(x1 - x6, amount_error);
|
||||
#ifndef NOT_FOR_PYTHON
|
||||
assertThrow(x1 - 123.45, amount_error);
|
||||
#endif // NOT_FOR_PYTHON
|
||||
assertThrow(x1 - 123L, amount_error);
|
||||
|
||||
assertEqual(amount_t("DM 0.00"), x3 - x3);
|
||||
|
|
@ -857,11 +865,7 @@ void AmountTestCase::testIntegerDivision()
|
|||
x1 /= amount_t(456L);
|
||||
assertEqual(string("0.269737"), x1.to_string());
|
||||
x1 /= 456L;
|
||||
#ifdef INTEGER_MATH
|
||||
assertEqual(string("0.00059152850877193"), x1.to_string());
|
||||
#else
|
||||
assertEqual(string("0.000591528162511542"), x1.to_string());
|
||||
#endif
|
||||
|
||||
amount_t x4("123456789123456789123456789");
|
||||
amount_t y4("56");
|
||||
|
|
@ -901,13 +905,9 @@ void AmountTestCase::testFractionalDivision()
|
|||
x1 /= amount_t("456.456");
|
||||
assertEqual(string("0.269736842105263"), x1.to_string());
|
||||
x1 /= amount_t("456.456");
|
||||
#ifdef INTEGER_MATH
|
||||
assertEqual(string("0.000590937225286255411255411255411255411"), x1.to_string());
|
||||
#else
|
||||
assertEqual(string("0.000590937225286255757169884601508201951"), x1.to_string());
|
||||
#endif
|
||||
x1 /= 456L;
|
||||
assertEqual(string("0.000001295914967733016252753094858358016252192982456140350877192982456140350877192982"), x1.to_string());
|
||||
assertEqual(string("0.000001295914967733017011337466214297678193292890066687335298289595263924317558590360"), x1.to_string());
|
||||
|
||||
amount_t x4("1234567891234567.89123456789");
|
||||
amount_t y4("56.789");
|
||||
|
|
@ -934,18 +934,18 @@ void AmountTestCase::testCommodityDivision()
|
|||
assertThrow(x1 / 0L, amount_error);
|
||||
assertEqual(amount_t("$0.00"), 0L / x1);
|
||||
assertEqual(x1, x1 / 1L);
|
||||
assertEqual(string("$0.00812216"), (1L / x1).to_string());
|
||||
assertEqual(string("$0.00812216"), (1L / x1).to_fullstring());
|
||||
assertEqual(- x1, x1 / -1L);
|
||||
assertEqual(string("$-0.00812216"), (-1L / x1).to_string());
|
||||
assertEqual(string("$0.26973382"), (x1 / y1).to_string());
|
||||
assertEqual(string("$-0.00812216"), (-1L / x1).to_fullstring());
|
||||
assertEqual(string("$0.26973382"), (x1 / y1).to_fullstring());
|
||||
assertEqual(string("$0.27"), (x1 / y1).to_string());
|
||||
assertEqual(string("$3.70735867"), (y1 / x1).to_string());
|
||||
assertEqual(string("$3.70735867"), (y1 / x1).to_fullstring());
|
||||
assertEqual(string("$3.71"), (y1 / x1).to_string());
|
||||
|
||||
// Internal amounts retain their precision, even when being
|
||||
// converted to strings
|
||||
assertEqual(string("$0.99727201"), (x1 / x2).to_string());
|
||||
assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_string());
|
||||
assertEqual(string("$0.99727201"), (x1 / x2).to_fullstring());
|
||||
assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_fullstring());
|
||||
assertEqual(string("$1.00"), (x1 / x2).to_string());
|
||||
assertEqual(string("$1.00273545321637426901"), (x2 / x1).to_string());
|
||||
|
||||
|
|
@ -959,10 +959,10 @@ void AmountTestCase::testCommodityDivision()
|
|||
x1 /= amount_t("123.12");
|
||||
assertEqual(string("$1.00"), x1.to_string());
|
||||
x1 /= amount_t("123.12");
|
||||
assertEqual(string("$0.00812216"), x1.to_string());
|
||||
assertEqual(string("$0.00812216"), x1.to_fullstring());
|
||||
assertEqual(string("$0.01"), x1.to_string());
|
||||
x1 /= 123L;
|
||||
assertEqual(string("$0.00006603"), x1.to_string());
|
||||
assertEqual(string("$0.00006603"), x1.to_fullstring());
|
||||
assertEqual(string("$0.00"), x1.to_string());
|
||||
|
||||
amount_t x6(internalAmount("$237235987235987.98723987235978"));
|
||||
|
|
@ -970,9 +970,9 @@ void AmountTestCase::testCommodityDivision()
|
|||
|
||||
assertEqual(amount_t("$1"), x7 / x7);
|
||||
assertEqual(string("$0.0019216115121765559608381226612019501046413574469262"),
|
||||
(x6 / x7).to_string());
|
||||
(x6 / x7).to_fullstring());
|
||||
assertEqual(string("$520.39654928343335571379527154924040947271699678158689736256"),
|
||||
(x7 / x6).to_string());
|
||||
(x7 / x6).to_fullstring());
|
||||
|
||||
assertValid(x1);
|
||||
assertValid(x2);
|
||||
|
|
@ -1098,177 +1098,6 @@ void AmountTestCase::testCommodityAbs()
|
|||
assertValid(x2);
|
||||
}
|
||||
|
||||
#ifndef NOT_FOR_PYTHON
|
||||
#ifdef INTEGER_MATH
|
||||
|
||||
void AmountTestCase::testFractionalRound()
|
||||
{
|
||||
amount_t x0;
|
||||
amount_t x1("1234.567890");
|
||||
|
||||
assertThrow(x0.precision(), amount_error);
|
||||
assertThrow(x0.round(), amount_error);
|
||||
assertThrow(x0.round(2), amount_error);
|
||||
assertThrow(x0.unround(), amount_error);
|
||||
assertEqual(amount_t::precision_t(6), x1.precision());
|
||||
|
||||
amount_t x1b(x1.unround());
|
||||
|
||||
assertEqual(x1b.precision(), x1b.unround().precision());
|
||||
|
||||
amount_t y7(x1.round(7));
|
||||
amount_t y6(x1.round(6));
|
||||
amount_t y5(x1.round(5));
|
||||
amount_t y4(x1.round(4));
|
||||
amount_t y3(x1.round(3));
|
||||
amount_t y2(x1.round(2));
|
||||
amount_t y1(x1.round(1));
|
||||
amount_t y0(x1.round(0));
|
||||
|
||||
assertEqual(amount_t::precision_t(6), y7.precision());
|
||||
assertEqual(amount_t::precision_t(6), y6.precision());
|
||||
assertEqual(amount_t::precision_t(5), y5.precision());
|
||||
assertEqual(amount_t::precision_t(4), y4.precision());
|
||||
assertEqual(amount_t::precision_t(3), y3.precision());
|
||||
assertEqual(amount_t::precision_t(2), y2.precision());
|
||||
assertEqual(amount_t::precision_t(1), y1.precision());
|
||||
assertEqual(amount_t::precision_t(0), y0.precision());
|
||||
|
||||
assertEqual(amount_t("1234.56789"), y7);
|
||||
assertEqual(amount_t("1234.56789"), y6);
|
||||
assertEqual(amount_t("1234.56789"), y5);
|
||||
assertEqual(amount_t("1234.5679"), y4);
|
||||
assertEqual(amount_t("1234.568"), y3);
|
||||
assertEqual(amount_t("1234.57"), y2);
|
||||
assertEqual(amount_t("1234.6"), y1);
|
||||
assertEqual(amount_t("1235"), y0);
|
||||
|
||||
amount_t x2("9876.543210");
|
||||
|
||||
assertEqual(amount_t("9876.543210"), x2.round(6));
|
||||
assertEqual(amount_t("9876.54321"), x2.round(5));
|
||||
assertEqual(amount_t("9876.5432"), x2.round(4));
|
||||
assertEqual(amount_t("9876.543"), x2.round(3));
|
||||
assertEqual(amount_t("9876.54"), x2.round(2));
|
||||
assertEqual(amount_t("9876.5"), x2.round(1));
|
||||
assertEqual(amount_t("9877"), x2.round(0));
|
||||
|
||||
amount_t x3("-1234.567890");
|
||||
|
||||
assertEqual(amount_t("-1234.56789"), x3.round(6));
|
||||
assertEqual(amount_t("-1234.56789"), x3.round(5));
|
||||
assertEqual(amount_t("-1234.5679"), x3.round(4));
|
||||
assertEqual(amount_t("-1234.568"), x3.round(3));
|
||||
assertEqual(amount_t("-1234.57"), x3.round(2));
|
||||
assertEqual(amount_t("-1234.6"), x3.round(1));
|
||||
assertEqual(amount_t("-1235"), x3.round(0));
|
||||
|
||||
amount_t x4("-9876.543210");
|
||||
|
||||
assertEqual(amount_t("-9876.543210"), x4.round(6));
|
||||
assertEqual(amount_t("-9876.54321"), x4.round(5));
|
||||
assertEqual(amount_t("-9876.5432"), x4.round(4));
|
||||
assertEqual(amount_t("-9876.543"), x4.round(3));
|
||||
assertEqual(amount_t("-9876.54"), x4.round(2));
|
||||
assertEqual(amount_t("-9876.5"), x4.round(1));
|
||||
assertEqual(amount_t("-9877"), x4.round(0));
|
||||
|
||||
amount_t x5("0.0000000000000000000000000000000000001");
|
||||
|
||||
assertEqual(amount_t("0.0000000000000000000000000000000000001"),
|
||||
x5.round(37));
|
||||
assertEqual(amount_t(0L), x5.round(36));
|
||||
|
||||
assertValid(x1);
|
||||
assertValid(x2);
|
||||
assertValid(x3);
|
||||
assertValid(x4);
|
||||
assertValid(x5);
|
||||
}
|
||||
|
||||
void AmountTestCase::testCommodityRound()
|
||||
{
|
||||
amount_t x1(internalAmount("$1234.567890"));
|
||||
|
||||
assertEqual(internalAmount("$1234.56789"), x1.round(6));
|
||||
assertEqual(internalAmount("$1234.56789"), x1.round(5));
|
||||
assertEqual(internalAmount("$1234.5679"), x1.round(4));
|
||||
assertEqual(internalAmount("$1234.568"), x1.round(3));
|
||||
assertEqual(amount_t("$1234.57"), x1.round(2));
|
||||
assertEqual(amount_t("$1234.6"), x1.round(1));
|
||||
assertEqual(amount_t("$1235"), x1.round(0));
|
||||
|
||||
amount_t x2(internalAmount("$9876.543210"));
|
||||
|
||||
assertEqual(internalAmount("$9876.543210"), x2.round(6));
|
||||
assertEqual(internalAmount("$9876.54321"), x2.round(5));
|
||||
assertEqual(internalAmount("$9876.5432"), x2.round(4));
|
||||
assertEqual(internalAmount("$9876.543"), x2.round(3));
|
||||
assertEqual(amount_t("$9876.54"), x2.round(2));
|
||||
assertEqual(amount_t("$9876.5"), x2.round(1));
|
||||
assertEqual(amount_t("$9877"), x2.round(0));
|
||||
|
||||
amount_t x3(internalAmount("$-1234.567890"));
|
||||
|
||||
assertEqual(internalAmount("$-1234.56789"), x3.round(6));
|
||||
assertEqual(internalAmount("$-1234.56789"), x3.round(5));
|
||||
assertEqual(internalAmount("$-1234.5679"), x3.round(4));
|
||||
assertEqual(internalAmount("$-1234.568"), x3.round(3));
|
||||
assertEqual(amount_t("$-1234.57"), x3.round(2));
|
||||
assertEqual(amount_t("$-1234.6"), x3.round(1));
|
||||
assertEqual(amount_t("$-1235"), x3.round(0));
|
||||
|
||||
amount_t x4(internalAmount("$-9876.543210"));
|
||||
|
||||
assertEqual(internalAmount("$-9876.543210"), x4.round(6));
|
||||
assertEqual(internalAmount("$-9876.54321"), x4.round(5));
|
||||
assertEqual(internalAmount("$-9876.5432"), x4.round(4));
|
||||
assertEqual(internalAmount("$-9876.543"), x4.round(3));
|
||||
assertEqual(amount_t("$-9876.54"), x4.round(2));
|
||||
assertEqual(amount_t("$-9876.5"), x4.round(1));
|
||||
assertEqual(amount_t("$-9877"), x4.round(0));
|
||||
|
||||
amount_t x5("$123.45");
|
||||
|
||||
x5 *= amount_t("100.12");
|
||||
|
||||
assertEqual(internalAmount("$12359.814"), x5);
|
||||
assertEqual(string("$12359.81"), x5.to_string());
|
||||
assertEqual(string("$12359.814"), x5.to_fullstring());
|
||||
assertEqual(string("$12359.814"), x5.unround().to_string());
|
||||
|
||||
assertValid(x1);
|
||||
assertValid(x2);
|
||||
assertValid(x3);
|
||||
assertValid(x4);
|
||||
assertValid(x5);
|
||||
}
|
||||
|
||||
void AmountTestCase::testCommodityDisplayRound()
|
||||
{
|
||||
amount_t x1("$0.85");
|
||||
amount_t x2("$0.1");
|
||||
|
||||
x1 *= amount_t("0.19");
|
||||
|
||||
assertNotEqual(amount_t("$0.16"), x1);
|
||||
assertEqual(internalAmount("$0.1615"), x1);
|
||||
assertEqual(string("$0.16"), x1.to_string());
|
||||
|
||||
assertEqual(amount_t("$0.10"), x2);
|
||||
assertNotEqual(internalAmount("$0.101"), x2);
|
||||
assertEqual(string("$0.10"), x2.to_string());
|
||||
|
||||
x1 *= 7L;
|
||||
|
||||
assertNotEqual(amount_t("$1.13"), x1);
|
||||
assertEqual(internalAmount("$1.1305"), x1);
|
||||
assertEqual(string("$1.13"), x1.to_string());
|
||||
}
|
||||
|
||||
#endif // INTEGER_MATH
|
||||
#endif
|
||||
|
||||
void AmountTestCase::testReduction()
|
||||
{
|
||||
amount_t x0;
|
||||
|
|
@ -1411,10 +1240,7 @@ void AmountTestCase::testFractionalConversion()
|
|||
amount_t x1("1234.56");
|
||||
amount_t x2("1234.5683787634678348734");
|
||||
|
||||
assertThrow(x1.to_long(), amount_error); // loses precision
|
||||
assertThrow(x2.to_double(), amount_error); // loses precision
|
||||
assertFalse(x2.fits_in_double());
|
||||
assertEqual(1234L, x1.to_long(true));
|
||||
assertEqual(1235L, x1.to_long());
|
||||
assertEqual(1234.56, x1.to_double());
|
||||
assertEqual(string("1234.56"), x1.to_string());
|
||||
assertEqual(string("1234.56"), x1.quantity_string());
|
||||
|
|
@ -1426,8 +1252,7 @@ void AmountTestCase::testCommodityConversion()
|
|||
{
|
||||
amount_t x1("$1234.56");
|
||||
|
||||
assertThrow(x1.to_long(), amount_error); // loses precision
|
||||
assertEqual(1234L, x1.to_long(true));
|
||||
assertEqual(1235L, x1.to_long());
|
||||
assertEqual(1234.56, x1.to_double());
|
||||
assertEqual(string("$1234.56"), x1.to_string());
|
||||
assertEqual(string("1234.56"), x1.quantity_string());
|
||||
|
|
@ -1436,6 +1261,7 @@ void AmountTestCase::testCommodityConversion()
|
|||
}
|
||||
|
||||
#ifndef NOT_FOR_PYTHON
|
||||
|
||||
void AmountTestCase::testPrinting()
|
||||
{
|
||||
amount_t x0;
|
||||
|
|
@ -1579,4 +1405,5 @@ void AmountTestCase::testXmlSerialization()
|
|||
|
||||
assertEqual(std::string("<amount>\n <commodity flags=\"PT\">\n <symbol>$</symbol>\n </commodity>\n <quantity>8192.34</quantity>\n</amount>\n"), storage.str());
|
||||
}
|
||||
|
||||
#endif // NOT_FOR_PYTHON
|
||||
|
|
|
|||
|
|
@ -32,11 +32,6 @@ class AmountTestCase : public CPPUNIT_NS::TestCase
|
|||
CPPUNIT_TEST(testCommodityNegation);
|
||||
CPPUNIT_TEST(testAbs);
|
||||
CPPUNIT_TEST(testCommodityAbs);
|
||||
#ifdef INTEGER_MATH
|
||||
CPPUNIT_TEST(testFractionalRound);
|
||||
CPPUNIT_TEST(testCommodityRound);
|
||||
CPPUNIT_TEST(testCommodityDisplayRound);
|
||||
#endif
|
||||
CPPUNIT_TEST(testReduction);
|
||||
CPPUNIT_TEST(testSign);
|
||||
CPPUNIT_TEST(testCommoditySign);
|
||||
|
|
|
|||
|
|
@ -75,22 +75,27 @@ void CommodityTestCase::testPriceHistory()
|
|||
|
||||
amt = x1.value(current_time);
|
||||
assertTrue(amt);
|
||||
assertEqual(amount_t("$2124.12"), *amt);
|
||||
assertEqual(string("$2124.12"), amt->to_string());
|
||||
#ifdef INTEGER_MATH
|
||||
assertEqual(string("$2124.12"), amt->to_fullstring());
|
||||
#else
|
||||
assertEqual(string("$2124.1220"), amt->to_fullstring());
|
||||
#endif
|
||||
|
||||
amt = x1.value(current_time, euro);
|
||||
assertTrue(amt);
|
||||
assertEqual(amount_t("EUR 1366.87"), *amt);
|
||||
assertEqual(string("EUR 1366.87"), amt->rounded().to_string());
|
||||
|
||||
// Add a newer Euro pricing
|
||||
aapl.add_price(jan17_07, amount_t("EUR 23.00"));
|
||||
|
||||
amt = x1.value(current_time, euro);
|
||||
assertTrue(amt);
|
||||
assertEqual(amount_t("EUR 2302.30"), *amt);
|
||||
assertEqual(string("EUR 2302.30"), amt->to_string());
|
||||
|
||||
amt = x1.value(current_time, cad);
|
||||
assertTrue(amt);
|
||||
assertEqual(amount_t("CAD 3223.22"), *amt);
|
||||
assertEqual(string("CAD 3223.22"), amt->to_string());
|
||||
#endif // NOT_FOR_PYTHON
|
||||
|
||||
assertValid(x1);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue