Added floored() and in_place_floor() methods

This commit is contained in:
John Wiegley 2009-11-11 03:39:53 -05:00
parent 4a4ff9d4b2
commit afe87280e0
8 changed files with 169 additions and 93 deletions

View file

@ -114,6 +114,99 @@ shared_ptr<commodity_pool_t> amount_t::current_pool;
bool amount_t::is_initialized = false;
namespace {
void stream_out_mpq(std::ostream& out,
mpq_t quant,
amount_t::precision_t prec,
int zeros_prec = -1,
const optional<commodity_t&>& comm = none)
{
char * buf = NULL;
try {
IF_DEBUG("amount.convert") {
char * tbuf = mpq_get_str(NULL, 10, quant);
DEBUG("amount.convert", "Rational to convert = " << tbuf);
std::free(tbuf);
}
// Convert the rational number to a floating-point, extending the
// floating-point to a large enough size to get a precise answer.
const std::size_t bits = (mpz_sizeinbase(mpq_numref(quant), 2) +
mpz_sizeinbase(mpq_denref(quant), 2));
mpfr_set_prec(tempfb, bits + amount_t::extend_by_digits*8);
mpfr_set_q(tempfb, quant, GMP_RNDN);
mpfr_asprintf(&buf, "%.*Rf", prec, tempfb);
DEBUG("amount.convert",
"mpfr_print = " << buf << " (precision " << prec << ")");
if (zeros_prec >= 0) {
string::size_type index = std::strlen(buf);
string::size_type point = 0;
for (string::size_type i = 0; i < index; i++) {
if (buf[i] == '.') {
point = i;
break;
}
}
if (point > 0) {
while (--index >= (point + 1 + zeros_prec) && buf[index] == '0')
buf[index] = '\0';
if (index >= (point + zeros_prec) && buf[index] == '.')
buf[index] = '\0';
}
}
if (comm) {
int integer_digits = 0;
if (comm && comm->has_flags(COMMODITY_STYLE_THOUSANDS)) {
// Count the number of integer digits
for (const char * p = buf; *p; p++) {
if (*p == '.')
break;
else if (*p != '-')
integer_digits++;
}
}
for (const char * p = buf; *p; p++) {
if (*p == '.') {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << ',';
else
out << *p;
assert(integer_digits <= 3);
}
else if (*p == '-') {
out << *p;
}
else {
out << *p;
if (integer_digits > 3 && --integer_digits % 3 == 0) {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << '.';
else
out << ',';
}
}
}
} else {
out << buf;
}
}
catch (...) {
if (buf != NULL)
mpfr_free_str(buf);
throw;
}
if (buf != NULL)
mpfr_free_str(buf);
}
}
void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
{
if (! is_initialized) {
@ -498,6 +591,19 @@ void amount_t::in_place_round()
set_keep_precision(false);
}
void amount_t::in_place_floor()
{
if (! quantity)
throw_(amount_error, _("Cannot floor an uninitialized amount"));
_dup();
std::ostringstream out;
stream_out_mpq(out, MP(quantity), 0);
mpq_set_str(MP(quantity), out.str().c_str(), 10);
}
void amount_t::in_place_unround()
{
if (! quantity)
@ -612,99 +718,6 @@ int amount_t::sign() const
return mpq_sgn(MP(quantity));
}
namespace {
void stream_out_mpq(std::ostream& out,
mpq_t quant,
amount_t::precision_t prec,
int zeros_prec = -1,
const optional<commodity_t&>& comm = none)
{
char * buf = NULL;
try {
IF_DEBUG("amount.convert") {
char * tbuf = mpq_get_str(NULL, 10, quant);
DEBUG("amount.convert", "Rational to convert = " << tbuf);
std::free(tbuf);
}
// Convert the rational number to a floating-point, extending the
// floating-point to a large enough size to get a precise answer.
const std::size_t bits = (mpz_sizeinbase(mpq_numref(quant), 2) +
mpz_sizeinbase(mpq_denref(quant), 2));
mpfr_set_prec(tempfb, bits + amount_t::extend_by_digits*8);
mpfr_set_q(tempfb, quant, GMP_RNDN);
mpfr_asprintf(&buf, "%.*Rf", prec, tempfb);
DEBUG("amount.convert",
"mpfr_print = " << buf << " (precision " << prec << ")");
if (zeros_prec >= 0) {
string::size_type index = std::strlen(buf);
string::size_type point = 0;
for (string::size_type i = 0; i < index; i++) {
if (buf[i] == '.') {
point = i;
break;
}
}
if (point > 0) {
while (--index >= (point + 1 + zeros_prec) && buf[index] == '0')
buf[index] = '\0';
if (index >= (point + zeros_prec) && buf[index] == '.')
buf[index] = '\0';
}
}
if (comm) {
int integer_digits = 0;
if (comm && comm->has_flags(COMMODITY_STYLE_THOUSANDS)) {
// Count the number of integer digits
for (const char * p = buf; *p; p++) {
if (*p == '.')
break;
else if (*p != '-')
integer_digits++;
}
}
for (const char * p = buf; *p; p++) {
if (*p == '.') {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << ',';
else
out << *p;
assert(integer_digits <= 3);
}
else if (*p == '-') {
out << *p;
}
else {
out << *p;
if (integer_digits > 3 && --integer_digits % 3 == 0) {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << '.';
else
out << ',';
}
}
}
} else {
out << buf;
}
}
catch (...) {
if (buf != NULL)
mpfr_free_str(buf);
throw;
}
if (buf != NULL)
mpfr_free_str(buf);
}
}
bool amount_t::is_zero() const
{
if (! quantity)

View file

@ -354,6 +354,15 @@ public:
*this = amount_t(to_string());
}
/** Yields an amount which has lost all of its extra precision, beyond what
the display precision of the commodity would have printed. */
amount_t floored() const {
amount_t temp(*this);
temp.in_place_floor();
return temp;
}
void in_place_floor();
/** Yields an amount whose display precision is never truncated, even
though its commodity normally displays only rounded values. */
amount_t unrounded() const {

View file

@ -339,6 +339,18 @@ public:
*this = temp;
}
balance_t floored() const {
balance_t temp(*this);
temp.in_place_floor();
return temp;
}
void in_place_floor() {
balance_t temp;
foreach (const amounts_map::value_type& pair, amounts)
temp += pair.second.floored();
*this = temp;
}
balance_t unrounded() const {
balance_t temp(*this);
temp.in_place_unround();

View file

@ -220,6 +220,10 @@ internal precision."))
.def("in_place_truncate", &amount_t::in_place_truncate,
return_internal_reference<>())
.def("floored", &amount_t::floored)
.def("in_place_floor", &amount_t::in_place_floor,
return_internal_reference<>())
.def("unrounded", &amount_t::unrounded)
.def("in_place_unround", &amount_t::in_place_unround,
return_internal_reference<>())

View file

@ -176,6 +176,10 @@ void export_balance()
.def("in_place_truncate", &balance_t::in_place_truncate,
return_internal_reference<>())
.def("floored", &balance_t::floored)
.def("in_place_floor", &balance_t::in_place_floor,
return_internal_reference<>())
.def("unrounded", &balance_t::unrounded)
.def("in_place_unround", &balance_t::in_place_unround,
return_internal_reference<>())

View file

@ -234,6 +234,8 @@ void export_value()
.def("in_place_round", &value_t::in_place_round)
.def("truncated", &value_t::truncated)
.def("in_place_truncate", &value_t::in_place_truncate)
.def("floored", &value_t::floored)
.def("in_place_floor", &value_t::in_place_floor)
.def("unrounded", &value_t::unrounded)
.def("in_place_unround", &value_t::in_place_unround)
.def("reduced", &value_t::reduced)

View file

@ -1439,6 +1439,31 @@ void value_t::in_place_truncate()
throw_(value_error, _("Cannot truncate %1") << label());
}
void value_t::in_place_floor()
{
switch (type()) {
case INTEGER:
return;
case AMOUNT:
as_amount_lval().in_place_floor();
return;
case BALANCE:
as_balance_lval().in_place_floor();
return;
case SEQUENCE: {
value_t temp;
foreach (const value_t& value, as_sequence())
temp.push_back(value.floored());
*this = temp;
return;
}
default:
break;
}
throw_(value_error, _("Cannot floor %1") << label());
}
void value_t::in_place_unround()
{
switch (type()) {

View file

@ -440,6 +440,13 @@ public:
}
void in_place_truncate();
value_t floored() const {
value_t temp(*this);
temp.in_place_floor();
return temp;
}
void in_place_floor();
value_t unrounded() const {
value_t temp(*this);
temp.in_place_unround();