diff --git a/src/Amount.cpp b/src/Amount.cpp index 88d40b912a..3e30ed9916 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -594,7 +594,7 @@ STAmount operator-(const STAmount& v1, const STAmount& v2) return STAmount(v1.name, v1.mCurrency, -fv, ov1, true); } -STAmount divide(const STAmount& num, const STAmount& den, const uint160& currencyOut) +STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint160& currencyOut) { if (den.isZero()) throw std::runtime_error("division by zero"); if (num.isZero()) return STAmount(currencyOut); @@ -602,10 +602,6 @@ STAmount divide(const STAmount& num, const STAmount& den, const uint160& currenc uint64 numVal = num.mValue, denVal = den.mValue; int numOffset = num.mOffset, denOffset = den.mOffset; - int finOffset = numOffset - denOffset; - if ((finOffset > 80) || (finOffset < 22)) - throw std::runtime_error("division produces out of range result"); - if (num.mIsNative) while (numVal < STAmount::cMinValue) { // Need to bring into range @@ -620,6 +616,10 @@ STAmount divide(const STAmount& num, const STAmount& den, const uint160& currenc --denOffset; } + int finOffset = numOffset - denOffset - 16; + if ((finOffset > cMaxOffset) || (finOffset < cMinOffset)) + throw std::runtime_error("division produces out of range result"); + // Compute (numerator * 10^16) / denominator CBigNum v; if ((BN_add_word(&v, numVal) != 1) || @@ -633,11 +633,11 @@ STAmount divide(const STAmount& num, const STAmount& den, const uint160& currenc assert(BN_num_bytes(&v) <= 64); if (num.mIsNegative != den.mIsNegative) - return -STAmount(currencyOut, v.getulong(), numOffset - denOffset - 16); - else return STAmount(currencyOut, v.getulong(), numOffset - denOffset - 16); + return -STAmount(currencyOut, v.getulong(), finOffset); + else return STAmount(currencyOut, v.getulong(), finOffset); } -STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut) +STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut) { if (v1.isZero() || v2.isZero()) return STAmount(currencyOut); @@ -699,7 +699,7 @@ STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currenc else return STAmount(currencyOut, v.getulong(), offset1 + offset2 + 14); } -uint64 getRate(const STAmount& offerOut, const STAmount& offerIn) +uint64 STAmount::getRate(const STAmount& offerOut, const STAmount& offerIn) { // Convert an offer into an index amount so they sort (lower is better) // offerOut = how much comes out of the offer, from the offeror to the taker // offerIn = how much goes into the offer, from the taker to the offeror @@ -711,7 +711,7 @@ uint64 getRate(const STAmount& offerOut, const STAmount& offerIn) return (ret << (64 - 8)) | r.getMantissa(); } -STAmount getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& paid) +STAmount STAmount::getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& paid) { // if someone is offering (offerOut) for (offerIn), and I pay (paid), how much do I get? offerIn.throwComparable(paid); @@ -748,7 +748,7 @@ STAmount getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& paid) return ret; } -STAmount getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed) +STAmount STAmount::getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed) { // Someone wants to get (needed) out of the offer, how much should they pay in? if (offerOut.isZero()) return STAmount(offerIn.getCurrency()); if (needed >= offerOut) return needed; @@ -756,7 +756,7 @@ STAmount getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAm return (ret > offerIn) ? offerIn : ret; } -static uint64 muldiv(uint64 a, uint64 b, uint64 c) +uint64 STAmount::muldiv(uint64 a, uint64 b, uint64 c) { // computes (a*b)/c rounding up - supports values up to 10^18 if (c == 0) throw std::runtime_error("underflow"); if ((a == 0) || (b == 0)) return 0; @@ -769,12 +769,12 @@ static uint64 muldiv(uint64 a, uint64 b, uint64 c) return v.getulong(); } -uint64 convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit) +uint64 STAmount::convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit) { // Convert an internal ledger/account quantity of native currency to a display amount return muldiv(internalAmount.getNValue(), totalInit, totalNow); } -STAmount convertToInternalAmount(uint64 displayAmount, uint64 totalNow, uint64 totalInit, +STAmount STAmount::convertToInternalAmount(uint64 displayAmount, uint64 totalNow, uint64 totalInit, const char *name) { // Convert a display/request currency amount to an internal amount return STAmount(name, muldiv(displayAmount, totalNow, totalInit)); @@ -970,6 +970,31 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test ) BOOST_TEST_MESSAGE("Amount CC Complete"); } +BOOST_AUTO_TEST_CASE( CurrencyMulDivTests ) +{ + // Test currency multiplication and division operations such as + // convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed, and getNeeded + + uint160 c(1); + if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(c, 1), STAmount(c, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(c, 10), STAmount(c, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(c, 1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(c, 10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(1), STAmount(c, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + if (STAmount::getRate(STAmount(10), STAmount(c, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + BOOST_FAIL("STAmount getrate fail"); + +} + BOOST_AUTO_TEST_SUITE_END() // vim:ts=4 diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index ceadb90e52..e562de31a6 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -231,6 +231,7 @@ protected: { ; } uint64 toUInt64() const; + static uint64 muldiv(uint64, uint64, uint64); public: STAmount(uint64 v = 0, bool isNeg = false) : mValue(v), mOffset(0), mIsNative(true), mIsNegative(isNeg) @@ -315,22 +316,22 @@ public: friend STAmount operator+(const STAmount& v1, const STAmount& v2); friend STAmount operator-(const STAmount& v1, const STAmount& v2); - friend STAmount divide(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); - friend STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); + static STAmount divide(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); + static STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); // Someone is offering X for Y, what is the rate? - friend uint64 getRate(const STAmount& offerOut, const STAmount& offerIn); + static uint64 getRate(const STAmount& offerOut, const STAmount& offerIn); // Someone is offering X for Y, I try to pay Z, how much do I get? // And what's left of the offer? And how much do I actually pay? - friend STAmount getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& paid); + static STAmount getClaimed(STAmount& offerOut, STAmount& offerIn, STAmount& paid); // Someone is offering X for Y, I need Z, how much do I pay - friend STAmount getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed); + static STAmount getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed); // Native currency conversions, to/from display format - friend uint64 convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit); - friend STAmount convertToInternalAmount(uint64 displayAmount, uint64 totalNow, uint64 totalInit, + static uint64 convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit); + static STAmount convertToInternalAmount(uint64 displayAmount, uint64 totalNow, uint64 totalInit, const char* name = NULL); static STAmount deserialize(SerializerIterator&);