support arbitrary precision after the decimal point; increase speed 0-10%

This commit is contained in:
John Wiegley 2004-08-21 01:43:50 -04:00
parent ec7f071bdd
commit 06ac87ab20
9 changed files with 243 additions and 243 deletions

402
amount.cc
View file

@ -4,68 +4,77 @@
#include "gmp.h" #include "gmp.h"
#define MAX_PRECISION 10
#define MPZ(x) ((MP_INT *)(x)) #define MPZ(x) ((MP_INT *)(x))
namespace ledger { namespace ledger {
static mpz_t full_divisor; static mpz_t divisor;
static mpz_t true_value; static mpz_t true_value;
static struct init_amounts { static struct init_amounts {
init_amounts() { init_amounts() {
mpz_init(full_divisor); mpz_init(divisor);
mpz_init(true_value); mpz_init(true_value);
mpz_ui_pow_ui(full_divisor, 10, MAX_PRECISION); mpz_set_ui(true_value, 1);
mpz_mul_ui(true_value, full_divisor, 1);
} }
#ifndef NO_CLEANUP #ifndef NO_CLEANUP
~init_amounts() { ~init_amounts() {
mpz_clear(full_divisor);
mpz_clear(true_value); mpz_clear(true_value);
mpz_clear(divisor);
} }
#endif #endif
} initializer; } initializer;
static void mpz_round(mpz_t out, mpz_t value, int precision) static void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
{ {
mpz_t divisor; // 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 quotient;
mpz_t remainder; mpz_t remainder;
mpz_init(divisor);
mpz_init(quotient); mpz_init(quotient);
mpz_init(remainder); mpz_init(remainder);
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision); DEBUG_CLASS("ledger.amount.round");
DEBUG_PRINT_("mpz_round: value " << value);
mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
DEBUG_PRINT_("mpz_round: divisor " << divisor);
mpz_tdiv_qr(quotient, remainder, value, divisor); mpz_tdiv_qr(quotient, remainder, value, divisor);
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision - 1); DEBUG_PRINT_("mpz_round: quotient " << quotient);
DEBUG_PRINT_("mpz_round: remainder " << remainder);
mpz_divexact_ui(divisor, divisor, 10);
mpz_mul_ui(divisor, divisor, 5); mpz_mul_ui(divisor, divisor, 5);
DEBUG_PRINT_("mpz_round: divisor " << divisor);
if (mpz_sgn(remainder) < 0) { if (mpz_sgn(remainder) < 0) {
mpz_ui_sub(divisor, 0, divisor); mpz_neg(divisor, divisor);
if (mpz_cmp(remainder, divisor) < 0) { if (mpz_cmp(remainder, divisor) < 0) {
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision); mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
mpz_add(remainder, divisor, remainder); mpz_add(remainder, divisor, remainder);
mpz_ui_sub(remainder, 0, remainder); mpz_ui_sub(remainder, 0, remainder);
DEBUG_PRINT_("mpz_round: + remainder " << remainder);
mpz_add(out, value, remainder); mpz_add(out, value, remainder);
} else { } else {
DEBUG_PRINT_("mpz_round: - remainder " << remainder);
mpz_sub(out, value, remainder); mpz_sub(out, value, remainder);
} }
} else { } else {
if (mpz_cmp(remainder, divisor) >= 0) { if (mpz_cmp(remainder, divisor) >= 0) {
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision); mpz_ui_pow_ui(divisor, 10, value_prec - round_prec);
mpz_sub(remainder, divisor, remainder); mpz_sub(remainder, divisor, remainder);
DEBUG_PRINT_("mpz_round: + remainder " << remainder);
mpz_add(out, value, remainder); mpz_add(out, value, remainder);
} else { } else {
DEBUG_PRINT_("mpz_round: - remainder " << remainder);
mpz_sub(out, value, remainder); mpz_sub(out, value, remainder);
} }
} }
mpz_clear(divisor);
mpz_clear(quotient); mpz_clear(quotient);
mpz_clear(remainder); mpz_clear(remainder);
} }
@ -75,6 +84,7 @@ amount_t::amount_t(const bool value)
{ {
if (value) { if (value) {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0;
quantity = new MP_INT; quantity = new MP_INT;
mpz_init_set(MPZ(quantity), true_value); mpz_init_set(MPZ(quantity), true_value);
} }
@ -86,8 +96,8 @@ amount_t::amount_t(const int value)
if (value != 0) { if (value != 0) {
_init(); _init();
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0;
mpz_set_si(MPZ(quantity), value); mpz_set_si(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
} }
} }
@ -97,8 +107,8 @@ amount_t::amount_t(const unsigned int value)
if (value != 0) { if (value != 0) {
_init(); _init();
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0;
mpz_set_ui(MPZ(quantity), value); mpz_set_ui(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
} }
} }
@ -108,8 +118,9 @@ amount_t::amount_t(const double value)
if (value != 0.0) { if (value != 0.0) {
_init(); _init();
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
// jww (2004-08-20): How do I calculate?
precision = 0;
mpz_set_d(MPZ(quantity), value); mpz_set_d(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
} }
} }
@ -134,6 +145,7 @@ void amount_t::_copy(const amount_t& amt)
mpz_init_set(MPZ(quantity), MPZ(amt.quantity)); mpz_init_set(MPZ(quantity), MPZ(amt.quantity));
} }
commodity = amt.commodity; commodity = amt.commodity;
precision = amt.precision;
assert(commodity); assert(commodity);
} }
@ -155,8 +167,12 @@ amount_t& amount_t::operator=(const char * value)
// assignment operator // assignment operator
amount_t& amount_t::operator=(const amount_t& amt) amount_t& amount_t::operator=(const amount_t& amt)
{ {
if (amt.quantity) if (amt.quantity) {
_copy(amt); _copy(amt);
} else {
commodity = amt.commodity;
precision = amt.precision;
}
return *this; return *this;
} }
@ -167,9 +183,11 @@ amount_t& amount_t::operator=(const bool value)
_clear(); _clear();
quantity = NULL; quantity = NULL;
commodity = NULL; commodity = NULL;
precision = 0;
} }
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0;
quantity = new MP_INT; quantity = new MP_INT;
mpz_init_set(MPZ(quantity), true_value); mpz_init_set(MPZ(quantity), true_value);
} }
@ -183,11 +201,12 @@ amount_t& amount_t::operator=(const int value)
_clear(); _clear();
quantity = NULL; quantity = NULL;
commodity = NULL; commodity = NULL;
precision = 0;
} }
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0;
mpz_set_si(MPZ(quantity), value); mpz_set_si(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
} }
return *this; return *this;
} }
@ -199,11 +218,12 @@ amount_t& amount_t::operator=(const unsigned int value)
_clear(); _clear();
quantity = NULL; quantity = NULL;
commodity = NULL; commodity = NULL;
precision = 0;
} }
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0;
mpz_set_ui(MPZ(quantity), value); mpz_set_ui(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
} }
return *this; return *this;
} }
@ -215,48 +235,66 @@ amount_t& amount_t::operator=(const double value)
_clear(); _clear();
quantity = NULL; quantity = NULL;
commodity = NULL; commodity = NULL;
precision = 0;
} }
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
// jww (2004-08-20): How do I calculate?
precision = 0;
mpz_set_d(MPZ(quantity), value); mpz_set_d(MPZ(quantity), value);
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
} }
return *this; return *this;
} }
amount_t& amount_t::operator+=(const amount_t& amt) void amount_t::_resize(int prec)
{ {
if (amt.quantity) { if (prec == precision)
if (! quantity) { return;
_init();
commodity = amt.commodity;
}
if (commodity != amt.commodity) if (prec < precision) {
throw amount_error("Adding amounts with different commodities"); mpz_ui_pow_ui(divisor, 10, precision - prec);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor);
mpz_add(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); } else {
mpz_ui_pow_ui(divisor, 10, prec - precision);
mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
} }
return *this;
precision = prec;
} }
amount_t& amount_t::operator-=(const amount_t& amt)
{
if (amt.quantity) {
if (! quantity) {
_init();
commodity = amt.commodity;
}
if (commodity != amt.commodity) #define DEF_OPERATOR(OP, FUNC) \
throw amount_error("Subtracting amounts with different commodities"); amount_t& amount_t::operator OP(const amount_t& amt) \
{ \
mpz_sub(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); if (amt.quantity) { \
} if (! quantity) { \
return *this; _init(); \
commodity = amt.commodity; \
precision = amt.precision; \
} \
\
if (commodity != amt.commodity) \
throw amount_error("+/- amounts with different commodities"); \
\
if (precision == amt.precision) { \
FUNC(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); \
} \
else if (precision < amt.precision) { \
_resize(amt.precision); \
FUNC(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); \
} else { \
amount_t temp = amt; \
temp._resize(precision); \
FUNC(MPZ(quantity), MPZ(quantity), MPZ(temp.quantity)); \
} \
} \
return *this; \
} }
DEF_OPERATOR(+=, mpz_add)
DEF_OPERATOR(-=, mpz_sub)
// unary negation // unary negation
amount_t& amount_t::negate() amount_t& amount_t::negate()
{ {
@ -377,85 +415,53 @@ bool amount_t::operator==(const unsigned int num) const
} }
// comparisons between amounts // comparisons between amounts
bool amount_t::operator<(const amount_t& amt) const #define DEF_CMP_OPERATOR(OP) \
{ bool amount_t::operator OP(const amount_t& amt) const \
if (! quantity) // equivalent to zero { \
return amt > 0; if (! quantity) \
if (! amt.quantity) // equivalent to zero return amt > 0; \
return *this < 0; if (! amt.quantity) \
return *this < 0; \
if (commodity != amt.commodity) \
throw amount_error("Comparing amounts with different commodities"); if (commodity != amt.commodity) \
throw amount_error("Comparing amounts with different commodities"); \
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) < 0; \
if (precision == amt.precision) { \
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) OP 0; \
} \
else if (precision < amt.precision) { \
amount_t temp = *this; \
temp._resize(amt.precision); \
return mpz_cmp(MPZ(temp.quantity), MPZ(amt.quantity)) OP 0; \
} \
else { \
amount_t temp = amt; \
temp._resize(precision); \
return mpz_cmp(MPZ(quantity), MPZ(temp.quantity)) OP 0; \
} \
} }
bool amount_t::operator<=(const amount_t& amt) const DEF_CMP_OPERATOR(<)
{ DEF_CMP_OPERATOR(<=)
if (! quantity) // equivalent to zero DEF_CMP_OPERATOR(>)
return amt >= 0; DEF_CMP_OPERATOR(>=)
if (! amt.quantity) // equivalent to zero DEF_CMP_OPERATOR(==)
return *this <= 0;
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) <= 0;
}
bool amount_t::operator>(const amount_t& amt) const
{
if (! quantity) // equivalent to zero
return amt < 0;
if (! amt.quantity) // equivalent to zero
return *this > 0;
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) > 0;
}
bool amount_t::operator>=(const amount_t& amt) const
{
if (! quantity) // equivalent to zero
return amt <= 0;
if (! amt.quantity) // equivalent to zero
return *this >= 0;
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) >= 0;
}
bool amount_t::operator==(const amount_t& amt) const
{
if (! quantity && ! amt.quantity)
return true;
else if (! quantity || ! amt.quantity)
return false;
if (commodity != amt.commodity)
throw amount_error("Comparing amounts with different commodities");
return mpz_cmp(MPZ(quantity), MPZ(amt.quantity)) == 0;
}
amount_t::operator bool() const amount_t::operator bool() const
{ {
if (quantity) { if (quantity) {
assert(commodity); if (precision <= commodity->precision) {
mpz_t temp; return mpz_sgn(MPZ(quantity)) != 0;
mpz_t divisor; } else {
mpz_init_set(temp, MPZ(quantity)); assert(commodity);
mpz_init(divisor); mpz_t temp;
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - commodity->precision); mpz_init_set(temp, MPZ(quantity));
mpz_tdiv_q(temp, temp, divisor); mpz_ui_pow_ui(divisor, 10, precision - commodity->precision);
bool zero = mpz_sgn(temp) == 0; mpz_tdiv_q(temp, temp, divisor);
mpz_clear(divisor); bool zero = mpz_sgn(temp) == 0;
mpz_clear(temp); mpz_clear(temp);
return ! zero; return ! zero;
}
} else { } else {
return false; return false;
} }
@ -472,60 +478,44 @@ amount_t amount_t::value(const std::time_t moment) const
amount_t& amount_t::operator*=(const amount_t& amt) amount_t& amount_t::operator*=(const amount_t& amt)
{ {
if (! amt.quantity) if (! amt.quantity || ! quantity)
return *this; return *this;
if (! quantity) {
_init();
commodity = amt.commodity;
}
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), full_divisor); precision += amt.precision;
return *this; return *this;
} }
amount_t& amount_t::operator/=(const amount_t& amt) amount_t& amount_t::operator/=(const amount_t& amt)
{ {
if (! amt.quantity) if (! quantity)
return *this; return *this;
if (! quantity) { if (! amt.quantity)
_init(); throw amount_error("Divide by zero");
commodity = amt.commodity;
}
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor); // Increase the value's precision, to capture fractional parts after
// the divide.
mpz_ui_pow_ui(divisor, 10, amt.precision + 6);
mpz_mul(MPZ(quantity), MPZ(quantity), divisor);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
precision += 6;
return *this; return *this;
} }
amount_t& amount_t::operator%=(const amount_t& amt) amount_t amount_t::round(int prec) const
{ {
if (! amt.quantity) if (prec == -1)
return *this; prec = commodity->precision;
if (! quantity) { if (! quantity || precision <= prec) {
_init();
commodity = amt.commodity;
}
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
mpz_tdiv_r(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
return *this;
}
amount_t amount_t::round(int precision) const
{
if (! quantity) {
return *this; return *this;
} else { } else {
amount_t temp = *this; amount_t temp = *this;
mpz_round(MPZ(temp.quantity), MPZ(temp.quantity), mpz_round(MPZ(temp.quantity), MPZ(temp.quantity),
precision == -1 ? commodity->precision : precision); precision, prec == -1 ? commodity->precision : prec);
return temp; return temp;
} }
} }
@ -535,7 +525,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
mpz_t quotient; mpz_t quotient;
mpz_t rquotient; mpz_t rquotient;
mpz_t remainder; mpz_t remainder;
mpz_t divisor;
if (! amt.quantity) if (! amt.quantity)
return out; return out;
@ -543,44 +532,45 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
mpz_init(quotient); mpz_init(quotient);
mpz_init(rquotient); mpz_init(rquotient);
mpz_init(remainder); mpz_init(remainder);
mpz_init(divisor);
bool negative = false; bool negative = false;
// Ensure the value is rounded to the commodity's precision before // Ensure the value is rounded to the commodity's precision before
// outputting it. NOTE: `rquotient' is used here as a temp variable! // outputting it. NOTE: `rquotient' is used here as a temp variable!
if (amt.commodity->precision != MAX_PRECISION) if (amt.commodity->precision < amt.precision) {
mpz_round(rquotient, MPZ(amt.quantity), amt.commodity->precision); mpz_round(rquotient, MPZ(amt.quantity),
amt.precision, amt.commodity->precision);
mpz_tdiv_qr(quotient, remainder, rquotient, full_divisor); mpz_ui_pow_ui(divisor, 10, amt.precision - amt.commodity->precision);
mpz_tdiv_q(rquotient, rquotient, divisor);
if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) mpz_ui_pow_ui(divisor, 10, amt.commodity->precision);
negative = true; mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
}
mpz_abs(quotient, quotient); else if (amt.commodity->precision > amt.precision) {
mpz_abs(remainder, remainder); mpz_ui_pow_ui(divisor, 10, amt.commodity->precision - amt.precision);
mpz_mul(rquotient, MPZ(amt.quantity), divisor);
if (amt.commodity->precision == MAX_PRECISION) { mpz_ui_pow_ui(divisor, 10, amt.commodity->precision);
mpz_set(rquotient, remainder); mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
} else { }
assert(MAX_PRECISION - amt.commodity->precision > 0); else if (amt.precision) {
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - amt.commodity->precision); mpz_ui_pow_ui(divisor, 10, amt.precision);
mpz_tdiv_qr(rquotient, remainder, remainder, divisor); mpz_tdiv_qr(quotient, remainder, MPZ(amt.quantity), divisor);
}
else {
mpz_set(quotient, MPZ(amt.quantity));
mpz_set_ui(remainder, 0);
} }
bool odd_chars_in_symbol = false; if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) {
negative = true;
for (const char * p = amt.commodity->symbol.c_str(); mpz_abs(quotient, quotient);
*p; mpz_abs(remainder, remainder);
p++) }
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') { mpz_set(rquotient, remainder);
odd_chars_in_symbol = true;
break;
}
if (! (amt.commodity->flags & COMMODITY_STYLE_SUFFIXED)) { if (! (amt.commodity->flags & COMMODITY_STYLE_SUFFIXED)) {
if (odd_chars_in_symbol) if (amt.commodity->quote)
out << "\"" << amt.commodity->symbol << "\""; out << "\"" << amt.commodity->symbol << "\"";
else else
out << amt.commodity->symbol; out << amt.commodity->symbol;
@ -636,16 +626,18 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
mpz_clear(temp); mpz_clear(temp);
} }
out << ((amt.commodity->flags & COMMODITY_STYLE_EUROPEAN) ? ',' : '.'); if (amt.commodity->precision) {
out << ((amt.commodity->flags & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
out.width(amt.commodity->precision); out.width(amt.commodity->precision);
out.fill('0'); out.fill('0');
out << rquotient; out << rquotient;
}
if (amt.commodity->flags & COMMODITY_STYLE_SUFFIXED) { if (amt.commodity->flags & COMMODITY_STYLE_SUFFIXED) {
if (amt.commodity->flags & COMMODITY_STYLE_SEPARATED) if (amt.commodity->flags & COMMODITY_STYLE_SEPARATED)
out << " "; out << " ";
if (odd_chars_in_symbol) if (amt.commodity->quote)
out << "\"" << amt.commodity->symbol << "\""; out << "\"" << amt.commodity->symbol << "\"";
else else
out << amt.commodity->symbol; out << amt.commodity->symbol;
@ -654,7 +646,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
mpz_clear(quotient); mpz_clear(quotient);
mpz_clear(rquotient); mpz_clear(rquotient);
mpz_clear(remainder); mpz_clear(remainder);
mpz_clear(divisor);
return out; return out;
} }
@ -703,8 +694,7 @@ void amount_t::parse(std::istream& in)
std::string symbol; std::string symbol;
std::string quant; std::string quant;
unsigned int flags = COMMODITY_STYLE_DEFAULTS;; unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
unsigned int precision = MAX_PRECISION;
if (! quantity) if (! quantity)
_init(); _init();
@ -752,9 +742,7 @@ void amount_t::parse(std::istream& in)
} }
else { else {
precision = 0; precision = 0;
quant = quant + ".0";
} }
assert(precision <= MAX_PRECISION);
// Create the commodity if has not already been seen. // Create the commodity if has not already been seen.
commodity = commodity_t::find_commodity(symbol, true); commodity = commodity_t::find_commodity(symbol, true);
@ -766,24 +754,15 @@ void amount_t::parse(std::istream& in)
// flags telling how to parse it. // flags telling how to parse it.
int len = quant.length(); int len = quant.length();
int buf_len = len + MAX_PRECISION; char * buf = new char[len + 1];
char * buf = new char[buf_len];
std::memset(buf, '0', buf_len - 1); const char * p = quant.c_str();
std::strncpy(buf, quant.c_str(), len); char * t = buf;
if (flags & COMMODITY_STYLE_THOUSANDS) while (*p) {
while (char * t = if (*p == ',' || *p == '.')
std::strchr(buf, flags & COMMODITY_STYLE_EUROPEAN ? '.' : ',')) p++;
do { *t = *(t + 1); } while (*(t++ + 1)); *t++ = *p++;
char * t = std::strchr(buf, flags & COMMODITY_STYLE_EUROPEAN ? ',' : '.');
if (! t)
t = buf + len;
for (int prec = 0; prec < MAX_PRECISION; prec++) {
*t = *(t + 1);
t++;
} }
*t = '\0'; *t = '\0';
@ -812,13 +791,15 @@ void amount_t::write_quantity(std::ostream& out) const
mpz_export(buf, &size, 1, sizeof(int), 0, 0, MPZ(quantity)); mpz_export(buf, &size, 1, sizeof(int), 0, 0, MPZ(quantity));
len = size * sizeof(int); len = size * sizeof(int);
#endif #endif
assert(len);
out.write((char *)&len, sizeof(len)); out.write((char *)&len, sizeof(len));
out.write(buf, len); if (len) {
out.write(buf, len);
#ifndef WRITE_AMOUNTS_TEXTUALLY #ifndef WRITE_AMOUNTS_TEXTUALLY
char negative = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0; char negative = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
out.write(&negative, sizeof(negative)); out.write(&negative, sizeof(negative));
#endif #endif
out.write((char *)&precision, sizeof(precision));
}
} else { } else {
len = 0; len = 0;
out.write((char *)&len, sizeof(len)); out.write((char *)&len, sizeof(len));
@ -843,6 +824,7 @@ void amount_t::read_quantity(std::istream& in)
if (negative) if (negative)
mpz_neg(MPZ(quantity), MPZ(quantity)); mpz_neg(MPZ(quantity), MPZ(quantity));
#endif #endif
in.read((char *)&precision, sizeof(precision));
} else { } else {
if (quantity) if (quantity)
_clear(); _clear();

View file

@ -18,10 +18,12 @@ class amount_t
void _init(); void _init();
void _copy(const amount_t& amt); void _copy(const amount_t& amt);
void _clear(); void _clear();
void _resize(int prec);
public: public:
base_type quantity; // amount, to MAX_PRECISION base_type quantity; // amount, to MAX_PRECISION
commodity_t * commodity; unsigned short precision;
commodity_t * commodity;
bool valid() const { bool valid() const {
if (quantity) if (quantity)
@ -32,13 +34,15 @@ class amount_t
// constructors // constructors
amount_t(commodity_t * _commodity = NULL) amount_t(commodity_t * _commodity = NULL)
: quantity(NULL), commodity(_commodity) {} : quantity(NULL), precision(0), commodity(_commodity) {}
amount_t(const amount_t& amt) : quantity(NULL) { amount_t(const amount_t& amt) : quantity(NULL) {
if (amt.quantity) if (amt.quantity) {
_copy(amt); _copy(amt);
else } else {
commodity = amt.commodity; commodity = amt.commodity;
precision = amt.precision;
}
} }
amount_t(const std::string& value) { amount_t(const std::string& value) {
parse(value); parse(value);
@ -67,12 +71,11 @@ class amount_t
amount_t& operator=(const double value); amount_t& operator=(const double value);
// general methods // general methods
amount_t round(int precision = -1) const; amount_t round(int prec = -1) const;
// in-place arithmetic // in-place arithmetic
amount_t& operator*=(const amount_t& amt); amount_t& operator*=(const amount_t& amt);
amount_t& operator/=(const amount_t& amt); amount_t& operator/=(const amount_t& amt);
amount_t& operator%=(const amount_t& amt);
amount_t& operator+=(const amount_t& amt); amount_t& operator+=(const amount_t& amt);
amount_t& operator-=(const amount_t& amt); amount_t& operator-=(const amount_t& amt);
@ -87,11 +90,6 @@ class amount_t
temp /= amt; temp /= amt;
return temp; return temp;
} }
amount_t operator%(const amount_t& amt) const {
amount_t temp = *this;
temp %= amt;
return temp;
}
amount_t operator+(const amount_t& amt) const { amount_t operator+(const amount_t& amt) const {
amount_t temp = *this; amount_t temp = *this;
temp += amt; temp += amt;
@ -209,6 +207,7 @@ class commodity_t
typedef unsigned long ident_t; typedef unsigned long ident_t;
std::string symbol; std::string symbol;
bool quote;
std::string name; std::string name;
std::string note; std::string note;
unsigned short precision; unsigned short precision;
@ -244,9 +243,19 @@ class commodity_t
// Now the per-object constructor and methods // Now the per-object constructor and methods
commodity_t(const std::string& _symbol = "", commodity_t(const std::string& _symbol = "",
unsigned int _precision = 2, unsigned int _precision = 0,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS) unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: symbol(_symbol), precision(_precision), flags(_flags) {} : symbol(_symbol), quote(false), precision(_precision), flags(_flags) {
check_symbol();
}
void check_symbol() {
for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
quote = true;
return;
}
}
void add_price(const std::time_t date, const amount_t& price) { void add_price(const std::time_t date, const amount_t& price) {
history.insert(history_pair(date, price)); history.insert(history_pair(date, price));

View file

@ -385,12 +385,10 @@ bool format_account::disp_subaccounts_p(const account_t * account,
} }
bool format_account::display_account(const account_t * account, bool format_account::display_account(const account_t * account,
const item_predicate<account_t>& disp_pred, const item_predicate<account_t>& disp_pred)
const bool even_top)
{ {
// Never display the master account, or an account that has already // Never display an account that has already been displayed.
// been displayed. if (account->dflags & ACCOUNT_DISPLAYED)
if (! (account->parent || even_top) || account->dflags & ACCOUNT_DISPLAYED)
return false; return false;
// At this point, one of two possibilities exists: the account is a // At this point, one of two possibilities exists: the account is a

View file

@ -153,8 +153,7 @@ class format_account : public item_handler<account_t>
} }
static bool display_account(const account_t * account, static bool display_account(const account_t * account,
const item_predicate<account_t>& disp_pred, const item_predicate<account_t>& disp_pred);
const bool even_top = false);
virtual void flush() { virtual void flush() {
output_stream.flush(); output_stream.flush();
@ -162,8 +161,12 @@ class format_account : public item_handler<account_t>
virtual void operator()(account_t * account) { virtual void operator()(account_t * account) {
if (display_account(account, disp_pred)) { if (display_account(account, disp_pred)) {
format.format_elements(output_stream, details_t(account)); if (! account->parent) {
account->dflags |= ACCOUNT_DISPLAYED; account->dflags |= ACCOUNT_TO_DISPLAY;
} else {
format.format_elements(output_stream, details_t(account));
account->dflags |= ACCOUNT_DISPLAYED;
}
} }
} }
}; };

View file

@ -162,12 +162,16 @@ static void dataHandler(void *userData, const char *s, int len)
} }
case COMM_SYM: case COMM_SYM:
if (curr_comm) if (curr_comm) {
curr_comm->symbol = std::string(s, len); curr_comm->symbol = std::string(s, len);
else if (curr_account) curr_comm->check_symbol();
}
else if (curr_account) {
curr_account_comm = commodity_t::find_commodity(std::string(s, len)); curr_account_comm = commodity_t::find_commodity(std::string(s, len));
else if (curr_entry) }
else if (curr_entry) {
entry_comm = commodity_t::find_commodity(std::string(s, len)); entry_comm = commodity_t::find_commodity(std::string(s, len));
}
break; break;
case COMM_NAME: case COMM_NAME:

View file

@ -106,7 +106,8 @@ class entry_t
}; };
#define ACCOUNT_DISPLAYED 0x1 #define ACCOUNT_DISPLAYED 0x1
#define ACCOUNT_TO_DISPLAY 0x2
typedef std::map<const std::string, account_t *> accounts_map; typedef std::map<const std::string, account_t *> accounts_map;
typedef std::pair<const std::string, account_t *> accounts_pair; typedef std::pair<const std::string, account_t *> accounts_pair;

View file

@ -334,7 +334,7 @@ int main(int argc, char * argv[], char * envp[])
if (! config->show_subtotal) { if (! config->show_subtotal) {
if (! config->display_predicate.empty()) if (! config->display_predicate.empty())
config->display_predicate += "&"; config->display_predicate += "&";
config->display_predicate += "!l"; config->display_predicate += "l<=1";
} }
} }
else if (command == "E") { else if (command == "E") {
@ -591,9 +591,7 @@ int main(int argc, char * argv[], char * envp[])
journal->master->value = journal->master->total; journal->master->value = journal->master->total;
if (format_account::display_account(journal->master, if (journal->master->dflags & ACCOUNT_TO_DISPLAY) {
item_predicate<account_t>("T"),
true)) {
std::string end_format = "--------------------\n"; std::string end_format = "--------------------\n";
format.reset(end_format + f); format.reset(end_format + f);
format.format_elements(OUT(), details_t(journal->master)); format.format_elements(OUT(), details_t(journal->master));

View file

@ -154,8 +154,11 @@ bool finalize_entry(entry_t * entry)
x != entry->transactions.end(); x != entry->transactions.end();
x++) x++)
if (! ((*x)->flags & TRANSACTION_VIRTUAL) || if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
((*x)->flags & TRANSACTION_BALANCE)) ((*x)->flags & TRANSACTION_BALANCE)) {
DEBUG_PRINT("ledger.textual.finalize",
"item cost is " << (*x)->cost);
balance += (*x)->cost; balance += (*x)->cost;
}
// If one transaction of a two-line transaction is of a different // If one transaction of a two-line transaction is of a different
// commodity than the others, and it has no per-unit price, // commodity than the others, and it has no per-unit price,
@ -210,6 +213,8 @@ bool finalize_entry(entry_t * entry)
balance = 0; balance = 0;
} }
DEBUG_PRINT("ledger.textual.finalize", "balance is " << balance);
return ! balance; return ! balance;
} }

View file

@ -171,7 +171,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case DEPTH: case DEPTH:
if (details.account) if (details.account)
result = (unsigned int) (details.account->depth - 1); result = (unsigned int) details.account->depth;
else else
result = 0U; result = 0U;
break; break;