Added floored() and in_place_floor() methods
This commit is contained in:
parent
4a4ff9d4b2
commit
afe87280e0
8 changed files with 169 additions and 93 deletions
199
src/amount.cc
199
src/amount.cc
|
|
@ -114,6 +114,99 @@ shared_ptr<commodity_pool_t> amount_t::current_pool;
|
||||||
|
|
||||||
bool amount_t::is_initialized = false;
|
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)
|
void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
|
||||||
{
|
{
|
||||||
if (! is_initialized) {
|
if (! is_initialized) {
|
||||||
|
|
@ -498,6 +591,19 @@ void amount_t::in_place_round()
|
||||||
set_keep_precision(false);
|
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()
|
void amount_t::in_place_unround()
|
||||||
{
|
{
|
||||||
if (! quantity)
|
if (! quantity)
|
||||||
|
|
@ -612,99 +718,6 @@ int amount_t::sign() const
|
||||||
return mpq_sgn(MP(quantity));
|
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
|
bool amount_t::is_zero() const
|
||||||
{
|
{
|
||||||
if (! quantity)
|
if (! quantity)
|
||||||
|
|
|
||||||
|
|
@ -354,6 +354,15 @@ public:
|
||||||
*this = amount_t(to_string());
|
*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
|
/** Yields an amount whose display precision is never truncated, even
|
||||||
though its commodity normally displays only rounded values. */
|
though its commodity normally displays only rounded values. */
|
||||||
amount_t unrounded() const {
|
amount_t unrounded() const {
|
||||||
|
|
|
||||||
|
|
@ -339,6 +339,18 @@ public:
|
||||||
*this = temp;
|
*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 unrounded() const {
|
||||||
balance_t temp(*this);
|
balance_t temp(*this);
|
||||||
temp.in_place_unround();
|
temp.in_place_unround();
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,10 @@ internal precision."))
|
||||||
.def("in_place_truncate", &amount_t::in_place_truncate,
|
.def("in_place_truncate", &amount_t::in_place_truncate,
|
||||||
return_internal_reference<>())
|
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("unrounded", &amount_t::unrounded)
|
||||||
.def("in_place_unround", &amount_t::in_place_unround,
|
.def("in_place_unround", &amount_t::in_place_unround,
|
||||||
return_internal_reference<>())
|
return_internal_reference<>())
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,10 @@ void export_balance()
|
||||||
.def("in_place_truncate", &balance_t::in_place_truncate,
|
.def("in_place_truncate", &balance_t::in_place_truncate,
|
||||||
return_internal_reference<>())
|
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("unrounded", &balance_t::unrounded)
|
||||||
.def("in_place_unround", &balance_t::in_place_unround,
|
.def("in_place_unround", &balance_t::in_place_unround,
|
||||||
return_internal_reference<>())
|
return_internal_reference<>())
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,8 @@ void export_value()
|
||||||
.def("in_place_round", &value_t::in_place_round)
|
.def("in_place_round", &value_t::in_place_round)
|
||||||
.def("truncated", &value_t::truncated)
|
.def("truncated", &value_t::truncated)
|
||||||
.def("in_place_truncate", &value_t::in_place_truncate)
|
.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("unrounded", &value_t::unrounded)
|
||||||
.def("in_place_unround", &value_t::in_place_unround)
|
.def("in_place_unround", &value_t::in_place_unround)
|
||||||
.def("reduced", &value_t::reduced)
|
.def("reduced", &value_t::reduced)
|
||||||
|
|
|
||||||
25
src/value.cc
25
src/value.cc
|
|
@ -1439,6 +1439,31 @@ void value_t::in_place_truncate()
|
||||||
throw_(value_error, _("Cannot truncate %1") << label());
|
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()
|
void value_t::in_place_unround()
|
||||||
{
|
{
|
||||||
switch (type()) {
|
switch (type()) {
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,13 @@ public:
|
||||||
}
|
}
|
||||||
void in_place_truncate();
|
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 unrounded() const {
|
||||||
value_t temp(*this);
|
value_t temp(*this);
|
||||||
temp.in_place_unround();
|
temp.in_place_unround();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue