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;
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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<>())
|
||||
|
|
|
|||
|
|
@ -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<>())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
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());
|
||||
}
|
||||
|
||||
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()) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue