expose 2^52 amount value limit in header, check that limit in test cases, add TALER_amount_multiply and TALER_amount_divide2 operations
This commit is contained in:
parent
883b1fc70b
commit
249ba03c36
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
|||||||
|
taler-exchange (0.9.0-12) unstable; urgency=low
|
||||||
|
|
||||||
|
* Fix typo in taler-auditor shell script: clean before building.
|
||||||
|
|
||||||
|
-- Christian Grothoff <grothoff@gnu.org> Mon, 28 Jun 2021 14:02:10 +0100
|
||||||
|
|
||||||
taler-exchange (0.9.0-11) unstable; urgency=low
|
taler-exchange (0.9.0-11) unstable; urgency=low
|
||||||
|
|
||||||
* Fix typo in taler-auditor-sync.
|
* Fix typo in taler-auditor-sync.
|
||||||
|
@ -64,6 +64,11 @@ extern "C"
|
|||||||
*/
|
*/
|
||||||
#define TALER_AMOUNT_FRAC_LEN 8
|
#define TALER_AMOUNT_FRAC_LEN 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum legal 'value' for an amount, based on IEEE double (for JavaScript compatibility).
|
||||||
|
*/
|
||||||
|
#define TALER_AMOUNT_MAX_VALUE (1LLU << 52)
|
||||||
|
|
||||||
|
|
||||||
GNUNET_NETWORK_STRUCT_BEGIN
|
GNUNET_NETWORK_STRUCT_BEGIN
|
||||||
|
|
||||||
@ -332,6 +337,32 @@ TALER_amount_divide (struct TALER_Amount *result,
|
|||||||
const struct TALER_Amount *dividend,
|
const struct TALER_Amount *dividend,
|
||||||
uint32_t divisor);
|
uint32_t divisor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divide one amount by another. Note that this function
|
||||||
|
* may introduce a rounding error. It rounds down.
|
||||||
|
*
|
||||||
|
* @param dividend amount to divide
|
||||||
|
* @param divisor by what to divide, must be positive
|
||||||
|
* @return @a dividend / @a divisor, rounded down. -1 on currency missmatch,
|
||||||
|
* INT_MAX for division by zero
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
TALER_amount_divide2 (const struct TALER_Amount *dividend,
|
||||||
|
const struct TALER_Amount *divisor);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiply an @a amount by a @ factor.
|
||||||
|
*
|
||||||
|
* @param[out] result where to store @a amount * @a factor
|
||||||
|
* @param amount amount to multiply
|
||||||
|
* @param factor factor by which to multiply
|
||||||
|
*/
|
||||||
|
enum TALER_AmountArithmeticResult
|
||||||
|
TALER_amount_multiply (struct TALER_Amount *result,
|
||||||
|
const struct TALER_Amount *amount,
|
||||||
|
uint32_t factor);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize the given amount.
|
* Normalize the given amount.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014 Taler Systems SA
|
Copyright (C) 2014-2021 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -24,11 +24,6 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "taler_util.h"
|
#include "taler_util.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum legal 'value' for an amount, based on IEEE double (for JavaScript compatibility).
|
|
||||||
*/
|
|
||||||
#define MAX_AMOUNT_VALUE (1LLU << 52)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set @a a to "invalid".
|
* Set @a a to "invalid".
|
||||||
@ -44,14 +39,6 @@ invalidate (struct TALER_Amount *a)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse monetary amount, in the format "T:V.F".
|
|
||||||
*
|
|
||||||
* @param str amount string
|
|
||||||
* @param[out] amount amount to write the result to
|
|
||||||
* @return #GNUNET_OK if the string is a valid monetary amount specification,
|
|
||||||
* #GNUNET_SYSERR if it is invalid.
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_string_to_amount (const char *str,
|
TALER_string_to_amount (const char *str,
|
||||||
struct TALER_Amount *amount)
|
struct TALER_Amount *amount)
|
||||||
@ -128,8 +115,8 @@ TALER_string_to_amount (const char *str,
|
|||||||
n = *value - '0';
|
n = *value - '0';
|
||||||
if ( (amount->value * 10 < amount->value) ||
|
if ( (amount->value * 10 < amount->value) ||
|
||||||
(amount->value * 10 + n < amount->value) ||
|
(amount->value * 10 + n < amount->value) ||
|
||||||
(amount->value > MAX_AMOUNT_VALUE) ||
|
(amount->value > TALER_AMOUNT_MAX_VALUE) ||
|
||||||
(amount->value * 10 + n > MAX_AMOUNT_VALUE) )
|
(amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) )
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||||
"Value specified in amount `%s' is too large\n",
|
"Value specified in amount `%s' is too large\n",
|
||||||
@ -182,15 +169,6 @@ TALER_string_to_amount (const char *str,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse monetary amount, in the format "T:V.F".
|
|
||||||
* The result is stored in network byte order (NBO).
|
|
||||||
*
|
|
||||||
* @param str amount string
|
|
||||||
* @param[out] amount_nbo amount to write the result to
|
|
||||||
* @return #GNUNET_OK if the string is a valid amount specification,
|
|
||||||
* #GNUNET_SYSERR if it is invalid.
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_string_to_amount_nbo (const char *str,
|
TALER_string_to_amount_nbo (const char *str,
|
||||||
struct TALER_AmountNBO *amount_nbo)
|
struct TALER_AmountNBO *amount_nbo)
|
||||||
@ -207,12 +185,6 @@ TALER_string_to_amount_nbo (const char *str,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert amount from host to network representation.
|
|
||||||
*
|
|
||||||
* @param res where to store amount in network representation
|
|
||||||
* @param[out] d amount in host representation
|
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
TALER_amount_hton (struct TALER_AmountNBO *res,
|
TALER_amount_hton (struct TALER_AmountNBO *res,
|
||||||
const struct TALER_Amount *d)
|
const struct TALER_Amount *d)
|
||||||
@ -227,12 +199,6 @@ TALER_amount_hton (struct TALER_AmountNBO *res,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert amount from network to host representation.
|
|
||||||
*
|
|
||||||
* @param[out] res where to store amount in host representation
|
|
||||||
* @param dn amount in network representation
|
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
TALER_amount_ntoh (struct TALER_Amount *res,
|
TALER_amount_ntoh (struct TALER_Amount *res,
|
||||||
const struct TALER_AmountNBO *dn)
|
const struct TALER_AmountNBO *dn)
|
||||||
@ -247,14 +213,6 @@ TALER_amount_ntoh (struct TALER_Amount *res,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the value of "zero" in a particular currency.
|
|
||||||
*
|
|
||||||
* @param cur currency description
|
|
||||||
* @param[out] amount amount to write the result to
|
|
||||||
* @return #GNUNET_OK if @a cur is a valid currency specification,
|
|
||||||
* #GNUNET_SYSERR if it is invalid.
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_amount_get_zero (const char *cur,
|
TALER_amount_get_zero (const char *cur,
|
||||||
struct TALER_Amount *amount)
|
struct TALER_Amount *amount)
|
||||||
@ -274,15 +232,11 @@ TALER_amount_get_zero (const char *cur,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if the given amount is valid.
|
|
||||||
*
|
|
||||||
* @param amount amount to check
|
|
||||||
* @return #GNUNET_OK if @a amount is valid
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_amount_is_valid (const struct TALER_Amount *amount)
|
TALER_amount_is_valid (const struct TALER_Amount *amount)
|
||||||
{
|
{
|
||||||
|
if (amount->value > TALER_AMOUNT_MAX_VALUE)
|
||||||
|
return GNUNET_SYSERR;
|
||||||
return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
|
return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,15 +255,6 @@ test_valid_nbo (const struct TALER_AmountNBO *a)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if @a a1 and @a a2 are the same currency.
|
|
||||||
*
|
|
||||||
* @param a1 amount to test
|
|
||||||
* @param a2 amount to test
|
|
||||||
* @return #GNUNET_YES if @a a1 and @a a2 are the same currency
|
|
||||||
* #GNUNET_NO if the currencies are different,
|
|
||||||
* #GNUNET_SYSERR if either amount is invalid
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_amount_cmp_currency (const struct TALER_Amount *a1,
|
TALER_amount_cmp_currency (const struct TALER_Amount *a1,
|
||||||
const struct TALER_Amount *a2)
|
const struct TALER_Amount *a2)
|
||||||
@ -324,15 +269,6 @@ TALER_amount_cmp_currency (const struct TALER_Amount *a1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if @a a1 and @a a2 are the same currency, NBO variant.
|
|
||||||
*
|
|
||||||
* @param a1 amount to test
|
|
||||||
* @param a2 amount to test
|
|
||||||
* @return #GNUNET_YES if @a a1 and @a a2 are the same currency
|
|
||||||
* #GNUNET_NO if the currencies are different,
|
|
||||||
* #GNUNET_SYSERR if either amount is invalid
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
|
TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
|
||||||
const struct TALER_AmountNBO *a2)
|
const struct TALER_AmountNBO *a2)
|
||||||
@ -347,19 +283,6 @@ TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare the value/fraction of two amounts. Does not compare the currency.
|
|
||||||
* Comparing amounts of different currencies will cause the program to abort().
|
|
||||||
* If unsure, check with #TALER_amount_cmp_currency() first to be sure that
|
|
||||||
* the currencies of the two amounts are identical.
|
|
||||||
*
|
|
||||||
* @param a1 first amount
|
|
||||||
* @param a2 second amount
|
|
||||||
* @return result of the comparison,
|
|
||||||
* -1 if `a1 < a2`
|
|
||||||
* 1 if `a1 > a2`
|
|
||||||
* 0 if `a1 == a2`.
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
TALER_amount_cmp (const struct TALER_Amount *a1,
|
TALER_amount_cmp (const struct TALER_Amount *a1,
|
||||||
const struct TALER_Amount *a2)
|
const struct TALER_Amount *a2)
|
||||||
@ -390,19 +313,6 @@ TALER_amount_cmp (const struct TALER_Amount *a1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare the value/fraction of two amounts. Does not compare the currency.
|
|
||||||
* Comparing amounts of different currencies will cause the program to abort().
|
|
||||||
* If unsure, check with #TALER_amount_cmp_currency() first to be sure that
|
|
||||||
* the currencies of the two amounts are identical. NBO variant.
|
|
||||||
*
|
|
||||||
* @param a1 first amount
|
|
||||||
* @param a2 second amount
|
|
||||||
* @return result of the comparison
|
|
||||||
* -1 if `a1 < a2`
|
|
||||||
* 1 if `a1 > a2`
|
|
||||||
* 0 if `a1 == a2`.
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
|
TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
|
||||||
const struct TALER_AmountNBO *a2)
|
const struct TALER_AmountNBO *a2)
|
||||||
@ -419,14 +329,6 @@ TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform saturating subtraction of amounts.
|
|
||||||
*
|
|
||||||
* @param[out] diff where to store (@a a1 - @a a2), or invalid if @a a2 > @a a1
|
|
||||||
* @param a1 amount to subtract from
|
|
||||||
* @param a2 amount to subtract
|
|
||||||
* @return operation status, negative on failures
|
|
||||||
*/
|
|
||||||
enum TALER_AmountArithmeticResult
|
enum TALER_AmountArithmeticResult
|
||||||
TALER_amount_subtract (struct TALER_Amount *diff,
|
TALER_amount_subtract (struct TALER_Amount *diff,
|
||||||
const struct TALER_Amount *a1,
|
const struct TALER_Amount *a1,
|
||||||
@ -482,14 +384,6 @@ TALER_amount_subtract (struct TALER_Amount *diff,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform addition of amounts.
|
|
||||||
*
|
|
||||||
* @param[out] sum where to store @a a1 + @a a2, set to "invalid" on overflow
|
|
||||||
* @param a1 first amount to add
|
|
||||||
* @param a2 second amount to add
|
|
||||||
* @return operation status, negative on failures
|
|
||||||
*/
|
|
||||||
enum TALER_AmountArithmeticResult
|
enum TALER_AmountArithmeticResult
|
||||||
TALER_amount_add (struct TALER_Amount *sum,
|
TALER_amount_add (struct TALER_Amount *sum,
|
||||||
const struct TALER_Amount *a1,
|
const struct TALER_Amount *a1,
|
||||||
@ -500,7 +394,8 @@ TALER_amount_add (struct TALER_Amount *sum,
|
|||||||
struct TALER_Amount res;
|
struct TALER_Amount res;
|
||||||
|
|
||||||
if (GNUNET_YES !=
|
if (GNUNET_YES !=
|
||||||
TALER_amount_cmp_currency (a1, a2))
|
TALER_amount_cmp_currency (a1,
|
||||||
|
a2))
|
||||||
{
|
{
|
||||||
invalidate (sum);
|
invalidate (sum);
|
||||||
return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
|
return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
|
||||||
@ -509,8 +404,10 @@ TALER_amount_add (struct TALER_Amount *sum,
|
|||||||
diff and a1/a2 */
|
diff and a1/a2 */
|
||||||
n1 = *a1;
|
n1 = *a1;
|
||||||
n2 = *a2;
|
n2 = *a2;
|
||||||
if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
|
if ( (GNUNET_SYSERR ==
|
||||||
(GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
|
TALER_amount_normalize (&n1)) ||
|
||||||
|
(GNUNET_SYSERR ==
|
||||||
|
TALER_amount_normalize (&n2)) )
|
||||||
{
|
{
|
||||||
invalidate (sum);
|
invalidate (sum);
|
||||||
return TALER_AAR_INVALID_NORMALIZATION_FAILED;
|
return TALER_AAR_INVALID_NORMALIZATION_FAILED;
|
||||||
@ -526,7 +423,7 @@ TALER_amount_add (struct TALER_Amount *sum,
|
|||||||
invalidate (sum);
|
invalidate (sum);
|
||||||
return TALER_AAR_INVALID_RESULT_OVERFLOW;
|
return TALER_AAR_INVALID_RESULT_OVERFLOW;
|
||||||
}
|
}
|
||||||
if (res.value > MAX_AMOUNT_VALUE)
|
if (res.value > TALER_AMOUNT_MAX_VALUE)
|
||||||
{
|
{
|
||||||
/* too large to be legal */
|
/* too large to be legal */
|
||||||
invalidate (sum);
|
invalidate (sum);
|
||||||
@ -548,14 +445,6 @@ TALER_amount_add (struct TALER_Amount *sum,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize the given amount.
|
|
||||||
*
|
|
||||||
* @param[in,out] amount amount to normalize
|
|
||||||
* @return #GNUNET_OK if normalization worked
|
|
||||||
* #GNUNET_NO if value was already normalized
|
|
||||||
* #GNUNET_SYSERR if value was invalid or could not be normalized
|
|
||||||
*/
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_amount_normalize (struct TALER_Amount *amount)
|
TALER_amount_normalize (struct TALER_Amount *amount)
|
||||||
{
|
{
|
||||||
@ -569,7 +458,7 @@ TALER_amount_normalize (struct TALER_Amount *amount)
|
|||||||
amount->fraction %= TALER_AMOUNT_FRAC_BASE;
|
amount->fraction %= TALER_AMOUNT_FRAC_BASE;
|
||||||
amount->value += overflow;
|
amount->value += overflow;
|
||||||
if ( (amount->value < overflow) ||
|
if ( (amount->value < overflow) ||
|
||||||
(amount->value > MAX_AMOUNT_VALUE) )
|
(amount->value > TALER_AMOUNT_MAX_VALUE) )
|
||||||
{
|
{
|
||||||
invalidate (amount);
|
invalidate (amount);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
@ -600,12 +489,6 @@ amount_to_tail (const struct TALER_Amount *amount,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert amount to string.
|
|
||||||
*
|
|
||||||
* @param amount amount to convert to string
|
|
||||||
* @return freshly allocated string representation
|
|
||||||
*/
|
|
||||||
char *
|
char *
|
||||||
TALER_amount_to_string (const struct TALER_Amount *amount)
|
TALER_amount_to_string (const struct TALER_Amount *amount)
|
||||||
{
|
{
|
||||||
@ -640,13 +523,6 @@ TALER_amount_to_string (const struct TALER_Amount *amount)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert amount to string.
|
|
||||||
*
|
|
||||||
* @param amount amount to convert to string
|
|
||||||
* @return statically allocated buffer with string representation,
|
|
||||||
* NULL if the @a amount was invalid
|
|
||||||
*/
|
|
||||||
const char *
|
const char *
|
||||||
TALER_amount2s (const struct TALER_Amount *amount)
|
TALER_amount2s (const struct TALER_Amount *amount)
|
||||||
{
|
{
|
||||||
@ -685,14 +561,6 @@ TALER_amount2s (const struct TALER_Amount *amount)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Divide an amount by a @a divisor. Note that this function
|
|
||||||
* may introduce a rounding error!
|
|
||||||
*
|
|
||||||
* @param[out] result where to store @a dividend / @a divisor
|
|
||||||
* @param dividend amount to divide
|
|
||||||
* @param divisor by what to divide, must be positive
|
|
||||||
*/
|
|
||||||
void
|
void
|
||||||
TALER_amount_divide (struct TALER_Amount *result,
|
TALER_amount_divide (struct TALER_Amount *result,
|
||||||
const struct TALER_Amount *dividend,
|
const struct TALER_Amount *dividend,
|
||||||
@ -718,20 +586,109 @@ TALER_amount_divide (struct TALER_Amount *result,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
int
|
||||||
* Round the amount to something that can be transferred on the wire.
|
TALER_amount_divide2 (const struct TALER_Amount *dividend,
|
||||||
* The rounding mode is specified via the smallest transferable unit,
|
const struct TALER_Amount *divisor)
|
||||||
* which must only have a fractional part *or* only a value (either
|
{
|
||||||
* of the two must be zero!).
|
double approx;
|
||||||
*
|
double d;
|
||||||
* If the @a round_unit given is zero, we do nothing and return #GNUNET_NO.
|
double r;
|
||||||
*
|
int ret;
|
||||||
* @param[in,out] amount amount to round down
|
struct TALER_Amount tmp;
|
||||||
* @param[in] round_unit unit that should be rounded down to, and
|
struct TALER_Amount nxt;
|
||||||
* either value part or the faction must be zero
|
|
||||||
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
|
if (GNUNET_YES !=
|
||||||
* #GNUNET_SYSERR if the amount or currency or @a round_unit was invalid
|
TALER_amount_cmp_currency (dividend,
|
||||||
*/
|
divisor))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ( (0 == divisor->fraction) &&
|
||||||
|
(0 == divisor->value) )
|
||||||
|
return INT_MAX;
|
||||||
|
/* first, get rounded approximation */
|
||||||
|
d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE)
|
||||||
|
+ ( (double) dividend->fraction);
|
||||||
|
r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE)
|
||||||
|
+ ( (double) divisor->fraction);
|
||||||
|
approx = d / r;
|
||||||
|
if (approx > ((double) INT_MAX))
|
||||||
|
return INT_MAX; /* 'infinity' */
|
||||||
|
/* round down */
|
||||||
|
if (approx < 2)
|
||||||
|
ret = 0;
|
||||||
|
else
|
||||||
|
ret = (int) approx - 2;
|
||||||
|
/* Now do *exact* calculation, using well rounded-down factor as starting
|
||||||
|
point to avoid having to do too many steps. */
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_multiply (&tmp,
|
||||||
|
divisor,
|
||||||
|
ret));
|
||||||
|
/* in practice, this loop will only run for one or two iterations */
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
GNUNET_assert (0 <=
|
||||||
|
TALER_amount_add (&nxt,
|
||||||
|
&tmp,
|
||||||
|
divisor));
|
||||||
|
if (1 ==
|
||||||
|
TALER_amount_cmp (&nxt,
|
||||||
|
dividend))
|
||||||
|
break; /* nxt > dividend */
|
||||||
|
ret++;
|
||||||
|
tmp = nxt;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum TALER_AmountArithmeticResult
|
||||||
|
TALER_amount_multiply (struct TALER_Amount *result,
|
||||||
|
const struct TALER_Amount *amount,
|
||||||
|
uint32_t factor)
|
||||||
|
{
|
||||||
|
struct TALER_Amount in = *amount;
|
||||||
|
|
||||||
|
if (GNUNET_SYSERR ==
|
||||||
|
TALER_amount_normalize (&in))
|
||||||
|
return TALER_AAR_INVALID_NORMALIZATION_FAILED;
|
||||||
|
memcpy (result->currency,
|
||||||
|
amount->currency,
|
||||||
|
TALER_CURRENCY_LEN);
|
||||||
|
if ( (0 == factor) ||
|
||||||
|
( (0 == in.value) &&
|
||||||
|
(0 == in.fraction) ) )
|
||||||
|
{
|
||||||
|
result->value = 0;
|
||||||
|
result->fraction = 0;
|
||||||
|
return TALER_AAR_RESULT_ZERO;
|
||||||
|
}
|
||||||
|
result->value = in.value * ((uint64_t) factor);
|
||||||
|
if (in.value != result->value / factor)
|
||||||
|
return TALER_AAR_INVALID_RESULT_OVERFLOW;
|
||||||
|
{
|
||||||
|
/* This multiplication cannot overflow since both inputs are 32-bit values */
|
||||||
|
uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction);
|
||||||
|
uint64_t res;
|
||||||
|
|
||||||
|
res = tmp / TALER_AMOUNT_FRAC_BASE;
|
||||||
|
/* check for overflow */
|
||||||
|
if (result->value + res < result->value)
|
||||||
|
return TALER_AAR_INVALID_RESULT_OVERFLOW;
|
||||||
|
result->value += res;
|
||||||
|
result->fraction = tmp % TALER_AMOUNT_FRAC_BASE;
|
||||||
|
}
|
||||||
|
if (result->value > TALER_AMOUNT_MAX_VALUE)
|
||||||
|
return TALER_AAR_INVALID_RESULT_OVERFLOW;
|
||||||
|
/* This check should be redundant... */
|
||||||
|
GNUNET_assert (GNUNET_SYSERR !=
|
||||||
|
TALER_amount_normalize (result));
|
||||||
|
return TALER_AAR_RESULT_POSITIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_amount_round_down (struct TALER_Amount *amount,
|
TALER_amount_round_down (struct TALER_Amount *amount,
|
||||||
const struct TALER_Amount *round_unit)
|
const struct TALER_Amount *round_unit)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
(C) 2015 Taler Systems SA
|
(C) 2015, 2021 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -186,21 +186,27 @@ main (int argc,
|
|||||||
|
|
||||||
/* test addition with overflow */
|
/* test addition with overflow */
|
||||||
a1.fraction = TALER_AMOUNT_FRAC_BASE - 1;
|
a1.fraction = TALER_AMOUNT_FRAC_BASE - 1;
|
||||||
a1.value = UINT64_MAX - 5;
|
a1.value = TALER_AMOUNT_MAX_VALUE - 5;
|
||||||
a2.fraction = 2;
|
a2.fraction = 2;
|
||||||
a2.value = 5;
|
a2.value = 5;
|
||||||
GNUNET_assert (TALER_AAR_INVALID_RESULT_OVERFLOW ==
|
GNUNET_assert (TALER_AAR_INVALID_RESULT_OVERFLOW ==
|
||||||
TALER_amount_add (&a3, &a1, &a2));
|
TALER_amount_add (&a3,
|
||||||
|
&a1,
|
||||||
|
&a2));
|
||||||
|
|
||||||
/* test addition with underflow on fraction */
|
/* test addition with underflow on fraction */
|
||||||
a1.fraction = 1;
|
a1.fraction = 1;
|
||||||
a1.value = UINT64_MAX;
|
a1.value = TALER_AMOUNT_MAX_VALUE;
|
||||||
a2.fraction = 2;
|
a2.fraction = 2;
|
||||||
a2.value = 0;
|
a2.value = 0;
|
||||||
GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
|
GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
|
||||||
TALER_amount_subtract (&a3, &a1, &a2));
|
TALER_amount_subtract (&a3,
|
||||||
GNUNET_assert (UINT64_MAX - 1 == a3.value);
|
&a1,
|
||||||
GNUNET_assert (TALER_AMOUNT_FRAC_BASE - 1 == a3.fraction);
|
&a2));
|
||||||
|
GNUNET_assert (TALER_AMOUNT_MAX_VALUE - 1 ==
|
||||||
|
a3.value);
|
||||||
|
GNUNET_assert (TALER_AMOUNT_FRAC_BASE - 1 ==
|
||||||
|
a3.fraction);
|
||||||
|
|
||||||
/* test division */
|
/* test division */
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
@ -288,6 +294,53 @@ main (int argc,
|
|||||||
&r));
|
&r));
|
||||||
GNUNET_assert (0 == TALER_amount_cmp (&a1,
|
GNUNET_assert (0 == TALER_amount_cmp (&a1,
|
||||||
&a2));
|
&a2));
|
||||||
|
|
||||||
|
/* test multiplication */
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount ("BTC:0",
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (TALER_AAR_RESULT_ZERO ==
|
||||||
|
TALER_amount_multiply (&a2,
|
||||||
|
&a1,
|
||||||
|
42));
|
||||||
|
GNUNET_assert (0 == TALER_amount_cmp (&a1,
|
||||||
|
&a2));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount ("BTC:5.001",
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount ("BTC:5001",
|
||||||
|
&r));
|
||||||
|
GNUNET_assert (TALER_AAR_RESULT_POSITIVE ==
|
||||||
|
TALER_amount_multiply (&a2,
|
||||||
|
&a1,
|
||||||
|
1000));
|
||||||
|
GNUNET_assert (0 == TALER_amount_cmp (&r,
|
||||||
|
&a2));
|
||||||
|
GNUNET_assert (1000 ==
|
||||||
|
TALER_amount_divide2 (&a2,
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount ("BTC:5006.00099999",
|
||||||
|
&r));
|
||||||
|
GNUNET_assert (1000 ==
|
||||||
|
TALER_amount_divide2 (&r,
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount ("BTC:5000.99999999",
|
||||||
|
&r));
|
||||||
|
GNUNET_assert (999 ==
|
||||||
|
TALER_amount_divide2 (&r,
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount ("BTC:0",
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (INT_MAX ==
|
||||||
|
TALER_amount_divide2 (&a2,
|
||||||
|
&a1));
|
||||||
|
GNUNET_assert (0 ==
|
||||||
|
TALER_amount_divide2 (&a1,
|
||||||
|
&a2));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user