Reduce STAmount public interface (RIPD-867):

* Implement subtraction as addition to the additive inverse
* Do not allow comparison with, addition to or subtraction from integers
* Remove unused functions
* Convert member functions to free functions
* Isolate unit-test specific code into the unit test
This commit is contained in:
Nik Bougalis
2015-05-17 10:29:47 -07:00
parent 67b18e4bea
commit 6f5d8bba2d
19 changed files with 351 additions and 541 deletions

View File

@@ -70,21 +70,24 @@ public:
static std::uint64_t const uRateOne;
//--------------------------------------------------------------------------
STAmount(SerialIter& sit, SField const& name);
struct unchecked { };
STAmount(SerialIter& sit, SField const& name);
// Calls canonicalize
STAmount (SField const& name, Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative);
// Does not call canonicalize
// Do not call canonicalize
STAmount (SField const& name, Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative, unchecked);
STAmount (Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative, unchecked);
// Call canonicalize
STAmount (SField const& name, Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative);
STAmount (SField const& name, std::int64_t mantissa);
STAmount (SField const& name,
@@ -95,12 +98,12 @@ public:
STAmount (std::uint64_t mantissa = 0, bool negative = false);
STAmount (Issue const& issue,
std::uint64_t mantissa = 0, int exponent = 0, bool negative = false);
STAmount (Issue const& issue, std::uint64_t mantissa = 0, int exponent = 0,
bool negative = false);
// VFALCO Is this needed when we have the previous signature?
STAmount (Issue const& issue,
std::uint32_t mantissa, int exponent = 0, bool negative = false);
STAmount (Issue const& issue, std::uint32_t mantissa, int exponent = 0,
bool negative = false);
STAmount (Issue const& issue, std::int64_t mantissa, int exponent = 0);
@@ -125,14 +128,10 @@ private:
std::unique_ptr<STAmount>
construct (SerialIter&, SField const& name);
void
setSNValue (std::int64_t);
void set (std::int64_t v);
void canonicalize();
public:
static
STAmount
createFromInt64 (SField const& n, std::int64_t v);
//--------------------------------------------------------------------------
//
// Observers
@@ -148,7 +147,6 @@ public:
// These three are deprecated
Currency const& getCurrency() const { return mIssue.currency; }
Account const& getIssuer() const { return mIssue.account; }
bool isNative() const { return mIsNative; }
int
signum() const noexcept
@@ -165,11 +163,6 @@ public:
return STAmount (mIssue);
}
// VFALCO TODO This can be a free function or just call the
// member on the issue.
std::string
getHumanCurrency() const;
void
setJson (Json::Value&) const;
@@ -184,9 +177,6 @@ public:
return *this != zero;
}
bool isComparable (STAmount const&) const;
void throwComparable (STAmount const&) const;
STAmount& operator+= (STAmount const&);
STAmount& operator-= (STAmount const&);
@@ -196,18 +186,12 @@ public:
return *this;
}
friend STAmount operator+ (STAmount const& v1, STAmount const& v2);
friend STAmount operator- (STAmount const& v1, STAmount const& v2);
//--------------------------------------------------------------------------
//
// Modification
//
//--------------------------------------------------------------------------
// VFALCO TODO Remove this, it is only called from the unit test
void roundSelf();
void negate()
{
if (*this != zero)
@@ -216,7 +200,8 @@ public:
void clear()
{
// VFALCO: Why -100?
// The -100 is used to allow 0 to sort less than a small positive values
// which have a negative exponent.
mOffset = mIsNative ? 0 : -100;
mValue = 0;
mIsNegative = false;
@@ -243,10 +228,6 @@ public:
/** Set the Issue for this amount and update mIsNative. */
void setIssue (Issue const& issue);
// VFALCO TODO Rename to setValueOnly (it only sets mantissa and exponent)
// Make this private
bool setValue (std::string const& sAmount);
//--------------------------------------------------------------------------
//
// STBase
@@ -279,9 +260,6 @@ public:
{
return (mValue == 0) && mIsNative;
}
void canonicalize();
void set (std::int64_t v);
};
//------------------------------------------------------------------------------
@@ -290,15 +268,18 @@ public:
//
//------------------------------------------------------------------------------
STAmount
amountFromRate (std::uint64_t uRate);
// VFALCO TODO The parameter type should be Quality not uint64_t
STAmount
amountFromQuality (std::uint64_t rate);
STAmount
amountFromJson (SField const& name, Json::Value const& v);
amountFromString (Issue const& issue, std::string const& amount);
STAmount
amountFromRate (std::uint64_t uRate);
amountFromJson (SField const& name, Json::Value const& v);
bool
amountFromJsonNoThrow (STAmount& result, Json::Value const& jvSource);
@@ -323,20 +304,36 @@ isLegalNet (STAmount const& value)
//------------------------------------------------------------------------------
bool operator== (STAmount const& lhs, STAmount const& rhs);
bool operator!= (STAmount const& lhs, STAmount const& rhs);
bool operator< (STAmount const& lhs, STAmount const& rhs);
bool operator> (STAmount const& lhs, STAmount const& rhs);
bool operator<= (STAmount const& lhs, STAmount const& rhs);
bool operator>= (STAmount const& lhs, STAmount const& rhs);
// native currency only
bool operator< (STAmount const& lhs, std::uint64_t rhs);
bool operator> (STAmount const& lhs, std::uint64_t rhs);
bool operator<= (STAmount const& lhs, std::uint64_t rhs);
bool operator>= (STAmount const& lhs, std::uint64_t rhs);
inline
bool
operator!= (STAmount const& lhs, STAmount const& rhs)
{
return !(lhs == rhs);
}
inline
bool
operator> (STAmount const& lhs, STAmount const& rhs)
{
return rhs < lhs;
}
inline
bool
operator<= (STAmount const& lhs, STAmount const& rhs)
{
return !(rhs < lhs);
}
inline
bool
operator>= (STAmount const& lhs, STAmount const& rhs)
{
return !(lhs < rhs);
}
STAmount operator+ (STAmount const& lhs, std::uint64_t rhs);
STAmount operator- (STAmount const& lhs, std::uint64_t rhs);
STAmount operator- (STAmount const& value);
//------------------------------------------------------------------------------
@@ -345,6 +342,9 @@ STAmount operator- (STAmount const& value);
//
//------------------------------------------------------------------------------
STAmount operator+ (STAmount const& v1, STAmount const& v2);
STAmount operator- (STAmount const& v1, STAmount const& v2);
STAmount
divide (STAmount const& v1, STAmount const& v2, Issue const& issue);
@@ -367,10 +367,6 @@ divRound (STAmount const& v1, STAmount const& v2,
std::uint64_t
getRate (STAmount const& offerOut, STAmount const& offerIn);
// When the currency is XRP, the value in raw unsigned units.
std::uint64_t
getNValue(STAmount const& amount);
//------------------------------------------------------------------------------
inline bool isXRP(STAmount const& amount)

View File

@@ -37,8 +37,8 @@ static const std::uint64_t tenTo14 = 100000000000000ull;
static const std::uint64_t tenTo14m1 = tenTo14 - 1;
static const std::uint64_t tenTo17 = tenTo14 * 1000;
STAmount const saZero (noIssue(), 0u);
STAmount const saOne (noIssue(), 1u);
STAmount const saZero (noIssue(), 0);
STAmount const saOne (noIssue(), 1);
//------------------------------------------------------------------------------
static
@@ -58,16 +58,12 @@ getSNValue (STAmount const& amount)
return ret;
}
std::uint64_t
getNValue (STAmount const& amount)
static
bool
areComparable (STAmount const& v1, STAmount const& v2)
{
if (!amount.native ())
throw std::runtime_error ("amount is not native!");
if (amount.negative ())
throw std::runtime_error ("amount is negative!");
return amount.mantissa ();
return v1.native() == v2.native() &&
v1.issue().currency == v2.issue().currency;
}
STAmount::STAmount(SerialIter& sit, SField const& name)
@@ -146,6 +142,30 @@ STAmount::STAmount(SerialIter& sit, SField const& name)
canonicalize();
}
STAmount::STAmount (SField const& name, Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative, unchecked)
: STBase (name)
, mIssue (issue)
, mValue (mantissa)
, mOffset (exponent)
, mIsNative (native)
, mIsNegative (negative)
{
}
STAmount::STAmount (Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative, unchecked)
: mIssue (issue)
, mValue (mantissa)
, mOffset (exponent)
, mIsNative (native)
, mIsNegative (negative)
{
}
STAmount::STAmount (SField const& name, Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative)
@@ -159,18 +179,6 @@ STAmount::STAmount (SField const& name, Issue const& issue,
canonicalize();
}
STAmount::STAmount (SField const& name, Issue const& issue,
mantissa_type mantissa, exponent_type exponent,
bool native, bool negative, unchecked)
: STBase (name)
, mIssue (issue)
, mValue (mantissa)
, mOffset (exponent)
, mIsNative (native)
, mIsNegative (negative)
{
}
STAmount::STAmount (SField const& name, std::int64_t mantissa)
: STBase (name)
, mOffset (0)
@@ -247,37 +255,12 @@ STAmount::construct (SerialIter& sit, SField const& name)
return std::make_unique<STAmount>(sit, name);
}
STAmount
STAmount::createFromInt64 (SField const& name, std::int64_t value)
{
return value >= 0
? STAmount (name, static_cast<std::uint64_t>(value), false)
: STAmount (name, static_cast<std::uint64_t>(-value), true);
}
//------------------------------------------------------------------------------
//
// Operators
//
//------------------------------------------------------------------------------
bool STAmount::isComparable (STAmount const& t) const
{
// are these two STAmount instances in the same currency
if (mIsNative) return t.mIsNative;
if (t.mIsNative) return false;
return mIssue.currency == t.mIssue.currency;
}
void STAmount::throwComparable (STAmount const& t) const
{
// throw an exception if these two STAmount instances are incomparable
if (!isComparable (t))
throw std::runtime_error ("amounts are not comparable");
}
STAmount& STAmount::operator+= (STAmount const& a)
{
*this = *this + a;
@@ -292,7 +275,8 @@ STAmount& STAmount::operator-= (STAmount const& a)
STAmount operator+ (STAmount const& v1, STAmount const& v2)
{
v1.throwComparable (v2);
if (!areComparable (v1, v2))
throw std::runtime_error ("Can't add amounts that are't comparable!");
if (v2 == zero)
return v1;
@@ -300,21 +284,21 @@ STAmount operator+ (STAmount const& v1, STAmount const& v2)
if (v1 == zero)
{
// Result must be in terms of v1 currency and issuer.
return STAmount (v1.getFName (), v1.mIssue,
v2.mValue, v2.mOffset, v2.mIsNegative);
return { v1.getFName (), v1.issue (),
v2.mantissa (), v2.exponent (), v2.negative () };
}
if (v1.mIsNative)
return STAmount (v1.getFName (), getSNValue (v1) + getSNValue (v2));
if (v1.native ())
return { v1.getFName (), getSNValue (v1) + getSNValue (v2) };
int ov1 = v1.mOffset, ov2 = v2.mOffset;
std::int64_t vv1 = static_cast<std::int64_t>(v1.mValue);
std::int64_t vv2 = static_cast<std::int64_t>(v2.mValue);
int ov1 = v1.exponent (), ov2 = v2.exponent ();
std::int64_t vv1 = static_cast<std::int64_t>(v1.mantissa ());
std::int64_t vv2 = static_cast<std::int64_t>(v2.mantissa ());
if (v1.mIsNegative)
if (v1.negative ())
vv1 = -vv1;
if (v2.mIsNegative)
if (v2.negative ())
vv2 = -vv2;
while (ov1 < ov2)
@@ -335,145 +319,25 @@ STAmount operator+ (STAmount const& v1, STAmount const& v2)
std::int64_t fv = vv1 + vv2;
if ((fv >= -10) && (fv <= 10))
return STAmount (v1.getFName (), v1.mIssue);
return { v1.getFName (), v1.issue () };
if (fv >= 0)
return STAmount (v1.getFName (), v1.mIssue, fv, ov1, false);
return STAmount (v1.getFName (), v1.mIssue, -fv, ov1, true);
return STAmount { v1.getFName (), v1.issue (),
static_cast<std::uint64_t>(fv), ov1, false };
return STAmount { v1.getFName (), v1.issue (),
static_cast<std::uint64_t>(-fv), ov1, true };
}
STAmount operator- (STAmount const& v1, STAmount const& v2)
{
v1.throwComparable (v2);
if (v2 == zero)
return v1;
if (v2.mIsNative)
{
// XXX This could be better, check for overflow and that maximum range
// is covered.
return STAmount::createFromInt64 (
v1.getFName (), getSNValue (v1) - getSNValue (v2));
}
int ov1 = v1.mOffset, ov2 = v2.mOffset;
auto vv1 = static_cast<std::int64_t>(v1.mValue);
auto vv2 = static_cast<std::int64_t>(v2.mValue);
if (v1.mIsNegative)
vv1 = -vv1;
if (v2.mIsNegative)
vv2 = -vv2;
while (ov1 < ov2)
{
vv1 /= 10;
++ov1;
}
while (ov2 < ov1)
{
vv2 /= 10;
++ov2;
}
// this subtraction cannot overflow an std::int64_t, it can overflow an STAmount and the constructor will throw
std::int64_t fv = vv1 - vv2;
if ((fv >= -10) && (fv <= 10))
return STAmount (v1.getFName (), v1.mIssue);
if (fv >= 0)
return STAmount (v1.getFName (), v1.mIssue, fv, ov1, false);
return STAmount (v1.getFName (), v1.mIssue, -fv, ov1, true);
return v1 + (-v2);
}
//------------------------------------------------------------------------------
std::uint64_t const STAmount::uRateOne = getRate (STAmount (1), STAmount (1));
// Note: mIsNative and mIssue.currency must be set already!
bool
STAmount::setValue (std::string const& amount)
{
static boost::regex const reNumber (
"^" // the beginning of the string
"([-+]?)" // (optional) + or - character
"(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0)
"(\\.([0-9]+))?" // (optional) period followed by any number
"([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number
"$",
boost::regex_constants::optimize);
boost::smatch match;
if (!boost::regex_match (amount, match, reNumber))
{
WriteLog (lsWARNING, STAmount) <<
"Number not valid: \"" << amount << "\"";
return false;
}
// Match fields:
// 0 = whole input
// 1 = sign
// 2 = integer portion
// 3 = whole fraction (with '.')
// 4 = fraction (without '.')
// 5 = whole exponent (with 'e')
// 6 = exponent sign
// 7 = exponent number
try
{
// CHECKME: Why 32? Shouldn't this be 16?
if ((match[2].length () + match[4].length ()) > 32)
{
WriteLog (lsWARNING, STAmount) << "Overlong number: " << amount;
return false;
}
mIsNegative = (match[1].matched && (match[1] == "-"));
// Can't specify XRP using fractional representation
if (mIsNative && match[3].matched)
return false;
if (!match[4].matched) // integer only
{
mValue = beast::lexicalCastThrow <std::uint64_t> (std::string (match[2]));
mOffset = 0;
}
else
{
// integer and fraction
mValue = beast::lexicalCastThrow <std::uint64_t> (match[2] + match[4]);
mOffset = -(match[4].length ());
}
if (match[5].matched)
{
// we have an exponent
if (match[6].matched && (match[6] == "-"))
mOffset -= beast::lexicalCastThrow <int> (std::string (match[7]));
else
mOffset += beast::lexicalCastThrow <int> (std::string (match[7]));
}
canonicalize ();
WriteLog (lsTRACE, STAmount) <<
"Canonicalized \"" << amount << "\" to " << mValue << " : " << mOffset;
}
catch (...)
{
return false;
}
return true;
}
void
STAmount::setIssue (Issue const& issue)
{
@@ -481,28 +345,6 @@ STAmount::setIssue (Issue const& issue)
mIsNative = isXRP (*this);
}
std::string STAmount::getHumanCurrency () const
{
return to_string (mIssue.currency);
}
void
STAmount::setSNValue (std::int64_t v)
{
if (!mIsNative) throw std::runtime_error ("not native");
if (v > 0)
{
mIsNegative = false;
mValue = static_cast<std::uint64_t>(v);
}
else
{
mIsNegative = true;
mValue = static_cast<std::uint64_t>(-v);
}
}
// Convert an offer into an index amount so they sort by rate.
// A taker will take the best, lowest, rate first.
// (e.g. a taker will prefer pay 1 get 3 over pay 1 get 2.
@@ -543,7 +385,7 @@ void STAmount::setJson (Json::Value& elem) const
// It is an error for currency or issuer not to be specified for valid
// json.
elem[jss::value] = getText ();
elem[jss::currency] = getHumanCurrency ();
elem[jss::currency] = to_string (mIssue.currency);
elem[jss::issuer] = to_string (mIssue.account);
}
else
@@ -552,28 +394,6 @@ void STAmount::setJson (Json::Value& elem) const
}
}
// VFALCO What does this perplexing function do?
void STAmount::roundSelf ()
{
if (mIsNative)
return;
std::uint64_t valueDigits = mValue % 1000000000ull;
if (valueDigits == 1)
{
mValue -= 1;
if (mValue < cMinValue)
canonicalize ();
}
else if (valueDigits == 999999999ull)
{
mValue += 1;
if (mValue > cMaxValue)
canonicalize ();
}
}
//------------------------------------------------------------------------------
//
// STBase
@@ -586,7 +406,7 @@ STAmount::getFullText () const
std::string ret;
ret.reserve(64);
ret = getText () + "/" + getHumanCurrency ();
ret = getText () + "/" + to_string (mIssue.currency);
if (!mIsNative)
{
@@ -831,7 +651,7 @@ void STAmount::set (std::int64_t v)
STAmount
amountFromRate (std::uint64_t uRate)
{
return STAmount (noIssue(), uRate, -9, false);
return { noIssue(), uRate, -9, false };
}
STAmount
@@ -846,6 +666,70 @@ amountFromQuality (std::uint64_t rate)
return STAmount (noIssue(), mantissa, exponent);
}
STAmount
amountFromString (Issue const& issue, std::string const& amount)
{
static boost::regex const reNumber (
"^" // the beginning of the string
"([-+]?)" // (optional) + or - character
"(0|[1-9][0-9]*)" // a number (no leading zeroes, unless 0)
"(\\.([0-9]+))?" // (optional) period followed by any number
"([eE]([+-]?)([0-9]+))?" // (optional) E, optional + or -, any number
"$",
boost::regex_constants::optimize);
boost::smatch match;
if (!boost::regex_match (amount, match, reNumber))
throw std::runtime_error ("Number '" + amount + "' is not valid");
// Match fields:
// 0 = whole input
// 1 = sign
// 2 = integer portion
// 3 = whole fraction (with '.')
// 4 = fraction (without '.')
// 5 = whole exponent (with 'e')
// 6 = exponent sign
// 7 = exponent number
// CHECKME: Why 32? Shouldn't this be 16?
if ((match[2].length () + match[4].length ()) > 32)
throw std::runtime_error ("Number '" + amount + "' is overlong");
bool negative = (match[1].matched && (match[1] == "-"));
// Can't specify XRP using fractional representation
if (isXRP(issue) && match[3].matched)
throw std::runtime_error ("XRP must be specified in integral drops.");
std::uint64_t mantissa;
int exponent;
if (!match[4].matched) // integer only
{
mantissa = beast::lexicalCastThrow <std::uint64_t> (std::string (match[2]));
exponent = 0;
}
else
{
// integer and fraction
mantissa = beast::lexicalCastThrow <std::uint64_t> (match[2] + match[4]);
exponent = -(match[4].length ());
}
if (match[5].matched)
{
// we have an exponent
if (match[6].matched && (match[6] == "-"))
exponent -= beast::lexicalCastThrow <int> (std::string (match[7]));
else
exponent += beast::lexicalCastThrow <int> (std::string (match[7]));
}
return { issue, mantissa, exponent, negative };
}
STAmount
amountFromJson (SField const& name, Json::Value const& v)
{
@@ -906,6 +790,7 @@ amountFromJson (SField const& name, Json::Value const& v)
{
if (v.isObject ())
throw std::runtime_error ("XRP may not be specified as an object");
issue = xrpIssue ();
}
else
{
@@ -939,35 +824,18 @@ amountFromJson (SField const& name, Json::Value const& v)
}
else if (value.isString ())
{
if (native)
{
std::int64_t val = beast::lexicalCastThrow <std::int64_t> (
value.asString ());
auto const ret = amountFromString (issue, value.asString ());
if (val >= 0)
{
mantissa = val;
}
else
{
mantissa = -val;
negative = true;
}
}
else
{
STAmount amount (name, issue, mantissa, exponent,
native, negative, STAmount::unchecked{});
amount.setValue (value.asString ());
return amount;
}
mantissa = ret.mantissa ();
exponent = ret.exponent ();
negative = ret.negative ();
}
else
{
throw std::runtime_error ("invalid amount type");
}
return STAmount (name, issue, mantissa, exponent, native, negative);
return { name, issue, mantissa, exponent, native, negative };
}
bool
@@ -992,114 +860,42 @@ amountFromJsonNoThrow (STAmount& result, Json::Value const& jvSource)
//
//------------------------------------------------------------------------------
static
int
compare (STAmount const& lhs, STAmount const& rhs)
{
// Compares the value of a to the value of this STAmount, amounts must be comparable
if (lhs.negative() != rhs.negative())
return lhs.negative() ? -1 : 1;
if (lhs.mantissa() == 0)
{
if (rhs.negative())
return 1;
return (rhs.mantissa() != 0) ? -1 : 0;
}
if (rhs.mantissa() == 0) return 1;
if (lhs.exponent() > rhs.exponent()) return lhs.negative() ? -1 : 1;
if (lhs.exponent() < rhs.exponent()) return lhs.negative() ? 1 : -1;
if (lhs.mantissa() > rhs.mantissa()) return lhs.negative() ? -1 : 1;
if (lhs.mantissa() < rhs.mantissa()) return lhs.negative() ? 1 : -1;
return 0;
}
bool
operator== (STAmount const& lhs, STAmount const& rhs)
{
return lhs.isComparable (rhs) && lhs.negative() == rhs.negative() &&
lhs.exponent() == rhs.exponent() && lhs.mantissa() == rhs.mantissa();
}
bool
operator!= (STAmount const& lhs, STAmount const& rhs)
{
return lhs.exponent() != rhs.exponent() ||
lhs.mantissa() != rhs.mantissa() ||
lhs.negative() != rhs.negative() || ! lhs.isComparable (rhs);
return areComparable (lhs, rhs) &&
lhs.negative() == rhs.negative() &&
lhs.exponent() == rhs.exponent() &&
lhs.mantissa() == rhs.mantissa();
}
bool
operator< (STAmount const& lhs, STAmount const& rhs)
{
lhs.throwComparable (rhs);
return compare (lhs, rhs) < 0;
}
if (!areComparable (lhs, rhs))
throw std::runtime_error ("Can't compare amounts that are't comparable!");
bool
operator> (STAmount const& lhs, STAmount const& rhs)
{
lhs.throwComparable (rhs);
return compare (lhs, rhs) > 0;
}
if (lhs.negative() != rhs.negative())
return lhs.negative();
bool
operator<= (STAmount const& lhs, STAmount const& rhs)
{
lhs.throwComparable (rhs);
return compare (lhs, rhs) <= 0;
}
if (lhs.mantissa() == 0)
{
if (rhs.negative())
return false;
return rhs.mantissa() != 0;
}
bool
operator>= (STAmount const& lhs, STAmount const& rhs)
{
lhs.throwComparable (rhs);
return compare (lhs, rhs) >= 0;
}
// We know that lhs is non-zero and both sides have the same sign. Since
// rhs is zero (and thus not negative), lhs must, therefore, be strictly
// greater than zero. So if rhs is zero, the comparison must be false.
if (rhs.mantissa() == 0) return false;
// native currency only
if (lhs.exponent() > rhs.exponent()) return lhs.negative();
if (lhs.exponent() < rhs.exponent()) return ! lhs.negative();
if (lhs.mantissa() > rhs.mantissa()) return lhs.negative();
if (lhs.mantissa() < rhs.mantissa()) return ! lhs.negative();
bool
operator< (STAmount const& lhs, std::uint64_t rhs)
{
// VFALCO Why the cast?
return getSNValue (lhs) < static_cast<std::int64_t>(rhs);
}
bool
operator> (STAmount const& lhs, std::uint64_t rhs)
{
// VFALCO Why the cast?
return getSNValue (lhs) > static_cast<std::int64_t>(rhs);
}
bool
operator<= (STAmount const& lhs, std::uint64_t rhs)
{
// VFALCO TODO The cast looks dangerous, is it needed?
return getSNValue (lhs) <= static_cast<std::int64_t>(rhs);
}
bool
operator>= (STAmount const& lhs, std::uint64_t rhs)
{
// VFALCO TODO The cast looks dangerous, is it needed?
return getSNValue (lhs) >= static_cast<std::int64_t>(rhs);
}
STAmount
operator+ (STAmount const& lhs, std::uint64_t rhs)
{
// VFALCO TODO The cast looks dangerous, is it needed?
return STAmount (lhs.getFName (),
getSNValue (lhs) + static_cast<std::int64_t>(rhs));
}
STAmount
operator- (STAmount const& lhs, std::uint64_t rhs)
{
// VFALCO TODO The cast looks dangerous, is it needed?
return STAmount (lhs.getFName (),
getSNValue (lhs) - static_cast<std::int64_t>(rhs));
return false;
}
STAmount

View File

@@ -138,12 +138,6 @@ static Json::Value singleton_expected (std::string const& object,
"]' must be an object with a single key/object value.");
}
static Json::Value serialization_error (SField const& sField)
{
return RPC::make_error (rpcINVALID_PARAMS,
"Object '" + sField.getName () + "' failed to serialize.");
}
static Json::Value template_mismatch (SField const& sField)
{
return RPC::make_error (rpcINVALID_PARAMS,

View File

@@ -38,41 +38,68 @@ public:
}
//--------------------------------------------------------------------------
STAmount roundSelf (STAmount const& amount)
{
if (amount.native ())
return amount;
bool roundTest (int n, int d, int m)
std::uint64_t mantissa = amount.mantissa ();
std::uint64_t valueDigits = mantissa % 1000000000;
if (valueDigits == 1)
{
mantissa--;
if (mantissa < STAmount::cMinValue)
return { amount.issue (), mantissa, amount.exponent (),
amount.negative () };
return { amount.issue (), mantissa, amount.exponent (),
amount.native(), amount.negative (), STAmount::unchecked {} };
}
if (valueDigits == 999999999)
{
mantissa++;
if (mantissa > STAmount::cMaxValue)
return { amount.issue (), mantissa, amount.exponent (),
amount.negative () };
return { amount.issue (), mantissa, amount.exponent (),
amount.native(), amount.negative (), STAmount::unchecked {} };
}
return amount;
}
void roundTest (int n, int d, int m)
{
// check STAmount rounding
STAmount num (noIssue(), n);
STAmount den (noIssue(), d);
STAmount mul (noIssue(), m);
STAmount quot = divide (n, d, noIssue());
STAmount res = multiply (quot, mul, noIssue());
STAmount res = roundSelf (multiply (quot, mul, noIssue()));
expect (! res.isNative (), "Product should not be native");
res.roundSelf ();
expect (! res.native (), "Product should not be native");
STAmount cmp (noIssue(), (n * m) / d);
expect (! cmp.isNative (), "Comparison amount should not be native");
expect (! cmp.native (), "Comparison amount should not be native");
expect (cmp.issue().currency == res.issue().currency,
"Product and result should be comparable");
if (res != cmp)
{
cmp.throwComparable (res);
WriteLog (lsWARNING, STAmount) << "(" << num.getText () << "/" << den.getText () << ") X " << mul.getText () << " = "
<< res.getText () << " not " << cmp.getText ();
log <<
"(" << num.getText () << "/" << den.getText () <<
") X " << mul.getText () << " = " << res.getText () <<
" not " << cmp.getText ();
fail ("Rounding");
return false;
return;
}
else
{
pass ();
}
return true;
}
void mulTest (int a, int b)
@@ -81,21 +108,17 @@ public:
STAmount bb (noIssue(), b);
STAmount prod1 (multiply (aa, bb, noIssue()));
expect (! prod1.isNative ());
expect (! prod1.native ());
STAmount prod2 (noIssue(), static_cast<std::uint64_t> (a) * static_cast<std::uint64_t> (b));
if (prod1 != prod2)
{
WriteLog (lsWARNING, STAmount) << "nn(" << aa.getFullText () << " * " << bb.getFullText () << ") = " << prod1.getFullText ()
<< " not " << prod2.getFullText ();
log <<
"nn(" << aa.getFullText () << " * " << bb.getFullText () <<
") = " << prod1.getFullText () << " not " << prod2.getFullText ();
fail ("Multiplication result is not exact");
}
else
{
pass ();
}
}
//--------------------------------------------------------------------------
@@ -103,14 +126,15 @@ public:
void testSetValue (
std::string const& value, Issue const& issue, bool success = true)
{
STAmount amount (issue);
bool const result = amount.setValue (value);
expect (result == success, "parse " + value);
if (success)
try
{
STAmount const amount = amountFromString (issue, value);
expect (amount.getText () == value, "format " + value);
}
catch (std::exception const& e)
{
expect (!success, "parse " + value + " should fail");
}
}
void testSetValue ()
@@ -187,8 +211,8 @@ public:
unexpected (serializeAndDeserialize (zeroSt) != zeroSt, "STAmount fail");
unexpected (serializeAndDeserialize (one) != one, "STAmount fail");
unexpected (serializeAndDeserialize (hundred) != hundred, "STAmount fail");
unexpected (!zeroSt.isNative (), "STAmount fail");
unexpected (!hundred.isNative (), "STAmount fail");
unexpected (!zeroSt.native (), "STAmount fail");
unexpected (!hundred.native (), "STAmount fail");
unexpected (zeroSt != zero, "STAmount fail");
unexpected (one == zero, "STAmount fail");
unexpected (hundred == zero, "STAmount fail");
@@ -269,8 +293,8 @@ public:
unexpected (serializeAndDeserialize (zeroSt) != zeroSt, "STAmount fail");
unexpected (serializeAndDeserialize (one) != one, "STAmount fail");
unexpected (serializeAndDeserialize (hundred) != hundred, "STAmount fail");
unexpected (zeroSt.isNative (), "STAmount fail");
unexpected (hundred.isNative (), "STAmount fail");
unexpected (zeroSt.native (), "STAmount fail");
unexpected (hundred.native (), "STAmount fail");
unexpected (zeroSt != zero, "STAmount fail");
unexpected (one == zero, "STAmount fail");
unexpected (hundred == zero, "STAmount fail");