amount rounding a la Christian

This commit is contained in:
Florian Dold 2020-01-17 18:59:15 +01:00
parent ac2e40d049
commit 7378b5a081
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 60 additions and 41 deletions

View File

@ -88,7 +88,7 @@ static char *currency;
/** /**
* How many fractional digits does the currency use? * How many fractional digits does the currency use?
*/ */
static uint8_t currency_rounding_fractional_digits; static struct TALER_Amount currency_round_unit;
/** /**
* Our configuration. * Our configuration.
@ -2894,7 +2894,7 @@ check_wire_out_cb
/* Round down to amount supported by wire method */ /* Round down to amount supported by wire method */
GNUNET_break (TALER_amount_round_down (&final_amount, GNUNET_break (TALER_amount_round_down (&final_amount,
currency_rounding_fractional_digits)); &currency_round_unit));
/* Calculate the exchange's gain as the fees plus rounding differences! */ /* Calculate the exchange's gain as the fees plus rounding differences! */
if (GNUNET_OK != if (GNUNET_OK !=
@ -5205,28 +5205,29 @@ run (void *cls,
return; return;
} }
{ {
unsigned long long num; char *rounding_str;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg, GNUNET_CONFIGURATION_get_value_string (cfg,
"taler", "taler",
"CURRENCY_ROUNDING_FRACTIONAL_DIGITS", "CURRENCY_ROUND_UNIT",
&num)) &rounding_str))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"No [taler]/CURRENCY_ROUNDING_FRACTIONAL_DIGITS specified, defaulting to 2 digits.\n"); "No [taler]/CURRENCY_ROUND_UNIT specified, defaulting to '0.01'.\n");
currency_rounding_fractional_digits = 2; TALER_amount_get_zero (currency, &currency_round_unit);
currency_round_unit.fraction = TALER_AMOUNT_FRAC_BASE / 100;
} }
else if (num > TALER_AMOUNT_FRAC_LEN) else if (GNUNET_OK !=
TALER_string_to_amount (rounding_str,
&currency_round_unit))
{ {
global_ret = 1;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Value of CURRENCY_ROUNDING_FRACTIONAL_DIGITS too big.\n"); "Invalid amount `%s' specified in `TALER' under `CURRENCY_ROUND_UNIT'\n",
rounding_str);
GNUNET_free (rounding_str);
global_ret = 1;
return; return;
} }
else
{
currency_rounding_fractional_digits = (uint8_t) num;
}
} }
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (cfg, GNUNET_CONFIGURATION_get_value_time (cfg,

View File

@ -224,7 +224,7 @@ static char *exchange_currency_string;
/** /**
* How many fractional digits does the currency use? * How many fractional digits does the currency use?
*/ */
static uint8_t currency_rounding_fractional_digits; static struct TALER_Amount currency_round_unit;
/** /**
* What is the base URL of this exchange? * What is the base URL of this exchange?
@ -615,29 +615,31 @@ exchange_serve_process_config ()
} }
{ {
unsigned long long num; char *rounding_str;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg, GNUNET_CONFIGURATION_get_value_string (cfg,
"taler", "taler",
"CURRENCY_ROUNDING_FRACTIONAL_DIGITS", "CURRENCY_ROUND_UNIT",
&num)) &rounding_str))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"No [taler]/CURRENCY_ROUNDING_FRACTIONAL_DIGITS specified, defaulting to 2 digits.\n"); "No [taler]/CURRENCY_ROUND_UNIT specified, defaulting to '0.01'.\n");
currency_rounding_fractional_digits = 2; TALER_amount_get_zero (exchange_currency_string, &currency_round_unit);
currency_round_unit.fraction = TALER_AMOUNT_FRAC_BASE / 100;
} }
else if (num > TALER_AMOUNT_FRAC_LEN) else if (GNUNET_OK !=
TALER_string_to_amount (rounding_str,
&currency_round_unit))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Value of CURRENCY_ROUNDING_FRACTIONAL_DIGITS too big.\n"); "Invalid amount `%s' specified in `TALER' under `CURRENCY_ROUND_UNIT'\n",
rounding_str);
GNUNET_free (rounding_str);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
else
{
currency_rounding_fractional_digits = (uint8_t) num;
}
} }
if (NULL == if (NULL ==
(db_plugin = TALER_EXCHANGEDB_plugin_load (cfg))) (db_plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
{ {
@ -1120,7 +1122,7 @@ expired_reserve_cb (void *cls,
/* round down to enable transfer */ /* round down to enable transfer */
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
TALER_amount_round_down (&amount_without_fee, TALER_amount_round_down (&amount_without_fee,
currency_rounding_fractional_digits)) &currency_round_unit))
{ {
GNUNET_break (0); GNUNET_break (0);
global_ret = GNUNET_SYSERR; global_ret = GNUNET_SYSERR;
@ -1456,7 +1458,7 @@ run_aggregation (void *cls)
&au->wire_fee)) || &au->wire_fee)) ||
(GNUNET_SYSERR == (GNUNET_SYSERR ==
TALER_amount_round_down (&au->final_amount, TALER_amount_round_down (&au->final_amount,
currency_rounding_fractional_digits)) || &currency_round_unit)) ||
( (0 == au->final_amount.value) && ( (0 == au->final_amount.value) &&
(0 == au->final_amount.fraction) ) ) (0 == au->final_amount.fraction) ) )
{ {

View File

@ -311,15 +311,18 @@ TALER_amount2s (const struct TALER_Amount *amount);
/** /**
* Round the amount to something that can be transferred on the wire. * Round the amount to something that can be transferred on the wire.
* The rounding mode is specified via the smallest transferable unit,
* which must only have a fractional part.
* *
* @param[in,out] amount amount to round down * @param[in,out] amount amount to round down
* @param max_fractional_digits number of fractional digits to round down to * @param[in] round_unit unit that should be rounded down to,
* the value part of this amount must be zero
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid * #GNUNET_SYSERR if the amount or currency was invalid
*/ */
int int
TALER_amount_round_down (struct TALER_Amount *amount, TALER_amount_round_down (struct TALER_Amount *amount,
uint8_t max_fractional_digits); const struct TALER_Amount *round_unit);
#if 0 /* keep Emacsens' auto-indent happy */ #if 0 /* keep Emacsens' auto-indent happy */

View File

@ -674,23 +674,27 @@ TALER_amount_divide (struct TALER_Amount *result,
/** /**
* Round the amount to something that can be transferred on the wire. * Round the amount to something that can be transferred on the wire.
* The rounding mode is specified via the smallest transferable unit,
* which must only have a fractional part.
* *
* @param[in,out] amount amount to round down * @param[in,out] amount amount to round down
* @param max_fractional_digits number of fractional digits to round down to * @param[in] round_unit unit that should be rounded down to,
* the value part of this amount must be zero
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary, * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid * #GNUNET_SYSERR if the amount or currency was invalid
*/ */
int int
TALER_amount_round_down (struct TALER_Amount *amount, TALER_amount_round_down (struct TALER_Amount *amount,
uint8_t max_fractional_digits) const struct TALER_Amount *round_unit)
{ {
uint32_t delta; uint32_t delta;
uint32_t divisor = 1;
for (unsigned int i = 0; i < max_fractional_digits; i++) GNUNET_break (0 == round_unit->value);
divisor *= 10;
delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / divisor); if (0 == round_unit->fraction)
return GNUNET_OK;
delta = amount->fraction % round_unit->fraction;
if (0 == delta) if (0 == delta)
return GNUNET_NO; return GNUNET_NO;
amount->fraction -= delta; amount->fraction -= delta;

View File

@ -31,6 +31,7 @@ main (int argc,
struct TALER_Amount a1; struct TALER_Amount a1;
struct TALER_Amount a2; struct TALER_Amount a2;
struct TALER_Amount a3; struct TALER_Amount a3;
struct TALER_Amount r;
char *c; char *c;
GNUNET_log_setup ("test-amout", GNUNET_log_setup ("test-amout",
@ -236,6 +237,10 @@ main (int argc,
/* test rounding #1 */ /* test rounding #1 */
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:0.01",
&r));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:4.001", TALER_string_to_amount ("EUR:4.001",
&a1)); &a1));
@ -243,19 +248,23 @@ main (int argc,
TALER_string_to_amount ("EUR:4", TALER_string_to_amount ("EUR:4",
&a2)); &a2));
GNUNET_assert (GNUNET_OK == TALER_amount_round_down (&a1, 2)); GNUNET_assert (GNUNET_OK == TALER_amount_round_down (&a1, &r));
GNUNET_assert (GNUNET_NO == TALER_amount_round_down (&a1, 2)); GNUNET_assert (GNUNET_NO == TALER_amount_round_down (&a1, &r));
GNUNET_assert (0 == TALER_amount_cmp (&a1, &a2)); GNUNET_assert (0 == TALER_amount_cmp (&a1, &a2));
/* test rounding #2 */ /* test rounding #2 */
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:0.001",
&r));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:4.001", TALER_string_to_amount ("EUR:4.001",
&a1)); &a1));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:4.001", TALER_string_to_amount ("EUR:4.001",
&a2)); &a2));
GNUNET_assert (GNUNET_NO == TALER_amount_round_down (&a1, 3)); GNUNET_assert (GNUNET_NO == TALER_amount_round_down (&a1, &r));
GNUNET_assert (0 == TALER_amount_cmp (&a1, &a2)); GNUNET_assert (0 == TALER_amount_cmp (&a1, &a2));
return 0; return 0;