diff --git a/src/ripple/protocol/IOUAmount.h b/src/ripple/protocol/IOUAmount.h index 8dca03395..3be6a58a3 100644 --- a/src/ripple/protocol/IOUAmount.h +++ b/src/ripple/protocol/IOUAmount.h @@ -24,6 +24,7 @@ #include #include #include +#include #include using beast::zero; @@ -137,6 +138,9 @@ public: } }; +std::string +to_string (IOUAmount const& amount); + } #endif diff --git a/src/ripple/protocol/XRPAmount.h b/src/ripple/protocol/XRPAmount.h index 0c04f00ee..9c6e0afb6 100644 --- a/src/ripple/protocol/XRPAmount.h +++ b/src/ripple/protocol/XRPAmount.h @@ -25,6 +25,7 @@ #include #include #include +#include #include using beast::zero; @@ -127,6 +128,13 @@ public: } }; +inline +std::string +to_string (XRPAmount const& amount) +{ + return std::to_string (amount.drops ()); +} + /** Returns true if the amount does not exceed the initial XRP in existence. */ inline bool isLegalAmount (XRPAmount const& amount) diff --git a/src/ripple/protocol/impl/IOUAmount.cpp b/src/ripple/protocol/impl/IOUAmount.cpp index 207549242..e01fcbe40 100644 --- a/src/ripple/protocol/impl/IOUAmount.cpp +++ b/src/ripple/protocol/impl/IOUAmount.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include namespace ripple { @@ -145,4 +147,100 @@ IOUAmount::operator<(IOUAmount const& other) const return mantissa_ < other.mantissa_; } +std::string +to_string (IOUAmount const& amount) +{ + // keep full internal accuracy, but make more human friendly if possible + if (amount == zero) + return "0"; + + int const exponent = amount.exponent (); + auto mantissa = amount.mantissa (); + + // Use scientific notation for exponents that are too small or too large + if (((exponent != 0) && ((exponent < -25) || (exponent > -5)))) + { + std::string ret = std::to_string (mantissa); + ret.append (1, 'e'); + ret.append (std::to_string (exponent)); + return ret; + } + + bool negative = false; + + if (mantissa < 0) + { + mantissa = -mantissa; + negative = true; + } + + assert (exponent + 43 > 0); + + size_t const pad_prefix = 27; + size_t const pad_suffix = 23; + + std::string const raw_value (std::to_string (mantissa)); + std::string val; + + val.reserve (raw_value.length () + pad_prefix + pad_suffix); + val.append (pad_prefix, '0'); + val.append (raw_value); + val.append (pad_suffix, '0'); + + size_t const offset (exponent + 43); + + auto pre_from (val.begin ()); + auto const pre_to (val.begin () + offset); + + auto const post_from (val.begin () + offset); + auto post_to (val.end ()); + + // Crop leading zeroes. Take advantage of the fact that there's always a + // fixed amount of leading zeroes and skip them. + if (std::distance (pre_from, pre_to) > pad_prefix) + pre_from += pad_prefix; + + assert (post_to >= post_from); + + pre_from = std::find_if (pre_from, pre_to, + [](char c) + { + return c != '0'; + }); + + // Crop trailing zeroes. Take advantage of the fact that there's always a + // fixed amount of trailing zeroes and skip them. + if (std::distance (post_from, post_to) > pad_suffix) + post_to -= pad_suffix; + + assert (post_to >= post_from); + + post_to = std::find_if( + std::make_reverse_iterator (post_to), + std::make_reverse_iterator (post_from), + [](char c) + { + return c != '0'; + }).base(); + + std::string ret; + + if (negative) + ret.append (1, '-'); + + // Assemble the output: + if (pre_from == pre_to) + ret.append (1, '0'); + else + ret.append(pre_from, pre_to); + + if (post_to != post_from) + { + ret.append (1, '.'); + ret.append (post_from, post_to); + } + + return ret; +} + } diff --git a/src/ripple/protocol/tests/IOUAmount.test.cpp b/src/ripple/protocol/tests/IOUAmount.test.cpp index 45e110951..010e215e0 100644 --- a/src/ripple/protocol/tests/IOUAmount.test.cpp +++ b/src/ripple/protocol/tests/IOUAmount.test.cpp @@ -145,6 +145,21 @@ public: expect (n != -n); } + void testToString() + { + testcase("IOU strings"); + + expect(to_string(IOUAmount (-2, 0)) == "-2"); + expect(to_string(IOUAmount (0, 0)) == "0"); + expect(to_string(IOUAmount (2, 0)) == "2"); + expect(to_string(IOUAmount (25, -3)) == "0.025"); + expect(to_string(IOUAmount (-25, -3)) == "-0.025"); + expect(to_string(IOUAmount (25, 1)) == "250"); + expect(to_string(IOUAmount (-25, 1)) == "-250"); + expect(to_string(IOUAmount (2, 20)) == "2000000000000000e5"); + expect(to_string(IOUAmount (-2, -20)) == "-2000000000000000e-35"); + } + //-------------------------------------------------------------------------- void run () @@ -153,6 +168,7 @@ public: testSigNum (); testBeastZero (); testComparisons (); + testToString (); } };