From 25000ab3711e5e10e77cea7f0feb5c13aa03b9d4 Mon Sep 17 00:00:00 2001 From: abitmore Date: Thu, 25 Jan 2018 04:24:08 +0000 Subject: [PATCH] Refactor to_string functions, add unit tests. #144 --- libraries/app/CMakeLists.txt | 1 + libraries/app/database_api.cpp | 74 ++---- libraries/app/include/graphene/app/util.hpp | 43 ++++ libraries/app/util.cpp | 161 +++++++++++++ tests/tests/app_util_tests.cpp | 245 ++++++++++++++++++++ 5 files changed, 464 insertions(+), 60 deletions(-) create mode 100644 libraries/app/include/graphene/app/util.hpp create mode 100644 libraries/app/util.cpp create mode 100644 tests/tests/app_util_tests.cpp diff --git a/libraries/app/CMakeLists.txt b/libraries/app/CMakeLists.txt index 75b9ca52a9..db84e92ca6 100644 --- a/libraries/app/CMakeLists.txt +++ b/libraries/app/CMakeLists.txt @@ -4,6 +4,7 @@ file(GLOB EGENESIS_HEADERS "../egenesis/include/graphene/app/*.hpp") add_library( graphene_app api.cpp application.cpp + util.cpp database_api.cpp impacted.cpp plugin.cpp diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index abaf570b81..f2ef83c066 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -155,10 +156,7 @@ class database_api_impl : public std::enable_shared_from_this //private: - string uint128_amount_to_string( const fc::uint128& amount, const uint8_t precision )const; - string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote )const; - string price_diff_to_string( const price& old_price, const price& new_price, - const asset_object& _base, const asset_object& _quote )const; + static string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ); template void subscribe_to_item( const T& i )const @@ -1154,63 +1152,15 @@ void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b _market_subscriptions.erase(std::make_pair(a,b)); } -string database_api_impl::uint128_amount_to_string( const fc::uint128& amount, const uint8_t precision )const +string database_api_impl::price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote ) { try { - fc::uint128 scaled_precision = 1; - for( uint8_t i = 0; i < precision; ++i ) - scaled_precision *= 10; - FC_ASSERT(scaled_precision > 0); - - string result = string( amount / scaled_precision ); - auto decimals = amount % scaled_precision; - if( decimals > 0 ) - result += "." + string(scaled_precision + decimals).erase(0,1); - return result; -} FC_CAPTURE_AND_RETHROW( (amount)(precision) ) } - -string database_api_impl::price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote )const -{ try { - share_type base_amount = _price.base.amount; - share_type quote_amount = _price.quote.amount; - if( _price.base.asset_id != _base.id ) - std::swap( base_amount, quote_amount ); - - // times (10**18 * 10**3) so won't overflow but always have good accuracy - fc::uint128 price128 = fc::uint128( base_amount.value ) * uint64_t(1000000000000000000ll) * uint64_t(1000) / quote_amount.value; - - return uint128_amount_to_string( price128, 21 + _base.precision - _quote.precision ); -} FC_CAPTURE_AND_RETHROW( (_price)(_base)(_quote) ) } - -string database_api_impl::price_diff_to_string( const price& old_price, const price& new_price, - const asset_object& _base, const asset_object& _quote )const -{ try { - share_type old_base_amount = old_price.base.amount; - share_type old_quote_amount = old_price.quote.amount; - if( old_price.base.asset_id != _base.id ) - std::swap( old_base_amount, old_quote_amount ); - - share_type new_base_amount = new_price.base.amount; - share_type new_quote_amount = new_price.quote.amount; - if( new_price.base.asset_id != _base.id ) - std::swap( new_base_amount, new_quote_amount ); - - // change = new/old - 1 = (new_base/new_quote)/(old_base/old_quote) - 1 - // = (new_base * old_quote) / (new_quote * old_base) - 1 - // = (new_base * old_quote - new_quote * old_base) / (new_quote * old_base) - fc::uint128 new128 = fc::uint128( new_base_amount.value ) * old_quote_amount.value; - fc::uint128 old128 = fc::uint128( old_base_amount.value ) * new_quote_amount.value; - bool non_negative = (new128 >= old128); - fc::uint128 diff128; - if( non_negative ) - diff128 = new128 - old128; + if( _price.base.asset_id == _base.id && _price.quote.asset_id == _quote.id ) + return graphene::app::price_to_string( _price, _base.precision, _quote.precision ); + else if( _price.base.asset_id == _quote.id && _price.quote.asset_id == _base.id ) + return graphene::app::price_to_string( ~_price, _base.precision, _quote.precision ); else - diff128 = old128 - new128; - string diff_str = uint128_amount_to_string( diff128 * 10000 / old128, 2 ); - if( non_negative ) - return diff_str; - else - return "-" + diff_str; -} FC_CAPTURE_AND_RETHROW( (old_price)(new_price)(_base)(_quote) ) } + FC_ASSERT( !"bad parameters" ); +} FC_CAPTURE_AND_RETHROW( (_price)(_base)(_quote) ) } market_ticker database_api::get_ticker( const string& base, const string& quote )const { @@ -1246,12 +1196,16 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q if( itr != ticker_idx.end() ) { price latest_price = asset( itr->latest_base, itr->base ) / asset( itr->latest_quote, itr->quote ); + if( itr->base != assets[0]->id ) + latest_price = ~latest_price; result.latest = price_to_string( latest_price, *assets[0], *assets[1] ); if( itr->last_day_base != 0 && itr->last_day_quote != 0 // has trade data before 24 hours && ( itr->last_day_base != itr->latest_base || itr->last_day_quote != itr->latest_quote ) ) // price changed { price last_day_price = asset( itr->last_day_base, itr->base ) / asset( itr->last_day_quote, itr->quote ); - result.percent_change = price_diff_to_string( last_day_price, latest_price, *assets[0], *assets[1] ); + if( itr->base != assets[0]->id ) + last_day_price = ~last_day_price; + result.percent_change = price_diff_percent_string( last_day_price, latest_price ); } if( assets[0]->id == itr->base ) { diff --git a/libraries/app/include/graphene/app/util.hpp b/libraries/app/include/graphene/app/util.hpp new file mode 100644 index 0000000000..520ce6c502 --- /dev/null +++ b/libraries/app/include/graphene/app/util.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Abit More, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#pragma once + +#include + +#include + +#include + +namespace graphene { namespace app { + using namespace graphene::chain; + + typedef boost::multiprecision::uint256_t u256; + + u256 to256( const fc::uint128& t ); + fc::uint128 to_capped128( const u256& t ); + string uint128_amount_to_string( const fc::uint128& amount, const uint8_t precision ); + string price_to_string( const price& _price, const uint8_t base_precision, const uint8_t quote_precision); + string price_diff_percent_string( const price& old_price, const price& new_price ); + +} } diff --git a/libraries/app/util.cpp b/libraries/app/util.cpp new file mode 100644 index 0000000000..b3a3b9833a --- /dev/null +++ b/libraries/app/util.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Abit More, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + + +namespace graphene { namespace app { + +u256 to256( const fc::uint128& t ) +{ + u256 v(t.hi); + v <<= 64; + v += t.lo; + return v; +} + +fc::uint128 to_capped128( const u256& t ) +{ + static u256 max128 = to256( fc::uint128::max_value() ); + if( t >= max128 ) + return fc::uint128::max_value(); + fc::uint128 result; + u256 hi(t); + hi >>= 64; + result.hi = static_cast< uint64_t >( hi ); + u256 lo(t); + hi <<= 64; + lo -= hi; + result.lo = static_cast< uint64_t >( lo ); + return result; +} + +string uint128_amount_to_string( const fc::uint128& amount, const uint8_t precision ) +{ try { + string s = string( amount ); + if( precision == 0 || amount == 0 ) + return s; + + std::stringstream ss; + uint8_t pos = s.find_last_not_of( '0' ); // should be >= 0 + uint8_t len = s.size(); + if( len > precision ) + { + uint8_t left_len = len - precision; + ss << s.substr( 0, left_len ); + if( pos >= left_len ) + ss << '.' << s.substr( left_len, pos - left_len + 1 ); + } + else + { + ss << "0."; + for( uint8_t i = precision - len; i > 0; --i ) + ss << '0'; + ss << s.substr( 0, pos + 1 ); + } + return ss.str(); +} FC_CAPTURE_AND_RETHROW( (amount)(precision) ) } + +string price_to_string( const price& _price, const uint8_t base_precision, const uint8_t quote_precision ) +{ try { + if( _price.base.amount == 0 ) + return "0"; + FC_ASSERT( _price.base.amount >= 0 ); + FC_ASSERT( _price.quote.amount >= 0 ); + FC_ASSERT( base_precision <= 19 ); + FC_ASSERT( quote_precision <= 19 ); + price new_price = _price; + if( new_price.quote.amount == 0 ) + { + new_price.base.amount = std::numeric_limits::max(); + new_price.quote.amount = 1; + } + + // times (10**19) so won't overflow but have good accuracy + fc::uint128 price128 = fc::uint128( new_price.base.amount.value ) * uint64_t(10000000000000000000ULL) + / new_price.quote.amount.value; + + return uint128_amount_to_string( price128, 19 + base_precision - quote_precision ); +} FC_CAPTURE_AND_RETHROW( (_price)(base_precision)(quote_precision) ) } + +string price_diff_percent_string( const price& old_price, const price& new_price ) +{ try { + FC_ASSERT( old_price.base.asset_id == new_price.base.asset_id ); + FC_ASSERT( old_price.quote.asset_id == new_price.quote.asset_id ); + FC_ASSERT( old_price.base.amount >= 0 ); + FC_ASSERT( old_price.quote.amount >= 0 ); + FC_ASSERT( new_price.base.amount >= 0 ); + FC_ASSERT( new_price.quote.amount >= 0 ); + price old_price1 = old_price; + if( old_price.base.amount == 0 ) + { + old_price1.base.amount = 1; + old_price1.quote.amount = std::numeric_limits::max(); + } + else if( old_price.quote.amount == 0 ) + { + old_price1.base.amount = std::numeric_limits::max(); + old_price1.quote.amount = 1; + } + price new_price1 = new_price; + if( new_price.base.amount == 0 ) + { + new_price1.base.amount = 1; + new_price1.quote.amount = std::numeric_limits::max(); + } + else if( new_price.quote.amount == 0 ) + { + new_price1.base.amount = std::numeric_limits::max(); + new_price1.quote.amount = 1; + } + + // change = new/old - 1 = (new_base/new_quote)/(old_base/old_quote) - 1 + // = (new_base * old_quote) / (new_quote * old_base) - 1 + // = (new_base * old_quote - new_quote * old_base) / (new_quote * old_base) + fc::uint128 new128 = fc::uint128( new_price1.base.amount.value ) * old_price1.quote.amount.value; + fc::uint128 old128 = fc::uint128( old_price1.base.amount.value ) * new_price1.quote.amount.value; + bool non_negative = (new128 >= old128); + fc::uint128 diff128; + if( non_negative ) + diff128 = new128 - old128; + else + diff128 = old128 - new128; + static fc::uint128 max = fc::uint128::max_value() / 10000; + if( diff128 <= max ) + diff128 = diff128 * 10000 / old128; + else + { + u256 diff256 = to256( diff128 ); + diff256 *= 10000; + diff256 /= to256( old128 ); + diff128 = to_capped128( diff256 ); + } + string diff_str = uint128_amount_to_string( diff128, 2 ); // at most 2 decimal digits + if( non_negative || diff_str == "0" ) + return diff_str; + else + return "-" + diff_str; +} FC_CAPTURE_AND_RETHROW( (old_price)(new_price) ) } + +} } // graphene::app diff --git a/tests/tests/app_util_tests.cpp b/tests/tests/app_util_tests.cpp new file mode 100644 index 0000000000..9b913fd7cd --- /dev/null +++ b/tests/tests/app_util_tests.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2018 Abit More, and contributors. + * + * The MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include + +#include "../common/database_fixture.hpp" + +using namespace graphene::chain; +using namespace graphene::chain::test; +using namespace graphene::app; + +BOOST_FIXTURE_TEST_SUITE(app_util_tests, database_fixture) + +BOOST_AUTO_TEST_CASE(uint128_amount_to_string_test) { + + fc::uint128 max_u64( std::numeric_limits::max() ); + fc::uint128 min_gt_u64 = max_u64 + 1; + fc::uint128 one_u128 = max_u64 * 10; + fc::uint128 max_u128 = fc::uint128::max_value(); + //idump( ( uint128_amount_to_string( fc::uint128::max_value(), 0) ) ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 0), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 0), "1" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 0), "100" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 0), "1024000" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 0), "1234567890" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 0), "18446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 0), "18446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 0), "184467440737095516150" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 0), "340282366920938463463374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 1), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 1), "0.1" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 1), "10" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 1), "102400" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 1), "123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 1), "1844674407370955161.5" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 1), "1844674407370955161.6" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 1), "18446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 1), "34028236692093846346337460743176821145.5" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 2), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 2), "0.01" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 2), "1" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 2), "10240" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 2), "12345678.9" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 2), "184467440737095516.15" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 2), "184467440737095516.16" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 2), "1844674407370955161.5" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 2), "3402823669209384634633746074317682114.55" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 3), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 3), "0.001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 3), "0.1" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 3), "1024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 3), "1234567.89" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 3), "18446744073709551.615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 3), "18446744073709551.616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 3), "184467440737095516.15" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 3), "340282366920938463463374607431768211.455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 4), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 4), "0.0001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 4), "0.01" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 4), "102.4" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 4), "123456.789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 4), "1844674407370955.1615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 4), "1844674407370955.1616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 4), "18446744073709551.615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 4), "34028236692093846346337460743176821.1455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 9), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 9), "0.000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 9), "0.0000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 9), "0.001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 9), "1.23456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 9), "18446744073.709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 9), "18446744073.709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 9), "184467440737.09551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 9), "340282366920938463463374607431.768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 10), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 10), "0.0000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 10), "0.00000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 10), "0.0001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 10), "0.123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 10), "1844674407.3709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 10), "1844674407.3709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 10), "18446744073.709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 10), "34028236692093846346337460743.1768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 19), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 19), "0.0000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 19), "0.00000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 19), "0.0000000000001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 19), "0.000000000123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 19), "1.8446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 19), "1.8446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 19), "18.446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 19), "34028236692093846346.3374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 20), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 20), "0.00000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 20), "0.000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 20), "0.00000000000001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 20), "0.0000000000123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 20), "0.18446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 20), "0.18446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 20), "1.8446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 20), "3402823669209384634.63374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 21), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 21), "0.000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 21), "0.0000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 21), "0.000000000000001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 21), "0.00000000000123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 21), "0.018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 21), "0.018446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 21), "0.18446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 21), "340282366920938463.463374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 38), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 38), "0.00000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 38), "0.000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 38), "0.00000000000000000000000000000001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 38), "0.0000000000000000000000000000123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 38), "0.00000000000000000018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 38), "0.00000000000000000018446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 38), "0.0000000000000000018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 38), "3.40282366920938463463374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 39), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 39), "0.000000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 39), "0.0000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 39), "0.000000000000000000000000000000001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 39), "0.00000000000000000000000000000123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 39), "0.000000000000000000018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 39), "0.000000000000000000018446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 39), "0.00000000000000000018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 39), "0.340282366920938463463374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 40), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 40), "0.0000000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 40), "0.00000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1024000, 40), "0.0000000000000000000000000000000001024" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1234567890, 40), "0.000000000000000000000000000000123456789" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u64, 40), "0.0000000000000000000018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( min_gt_u64, 40), "0.0000000000000000000018446744073709551616" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( one_u128, 40), "0.000000000000000000018446744073709551615" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 40), "0.0340282366920938463463374607431768211455" ); + + BOOST_CHECK_EQUAL( uint128_amount_to_string( 0, 127), "0" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 1, 127), "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( 100, 127), "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001" ); + BOOST_CHECK_EQUAL( uint128_amount_to_string( max_u128, 127), "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000340282366920938463463374607431768211455" ); + +} + +BOOST_AUTO_TEST_CASE(price_to_string_test) { + + int64_t m = std::numeric_limits::max(); + int64_t n = -1; + int64_t x = m / 10000; + int64_t y = m / 2; + int64_t z = m - 1; + + int64_t a[11] = {n,0,1,2,3,10,200,x,y,z,m}; + price p[11][11]; + for( int i = 0; i < 11; ++i ) + for( int j = 0; j < 11; ++j ) + p[i][j] = price( asset( a[i] ), asset( a[j] ) ); + + for( int i = 0; i < 11; ++i ) + for( int j = 0; j < 11; ++j ) + { + if( i == 0 ) + { + GRAPHENE_REQUIRE_THROW( price_to_string( p[i][j], 0, 0 ), fc::exception ); + } + else if( i == 1 ) + { + BOOST_CHECK_EQUAL( price_to_string( p[i][j], 0, 0 ), "0" ); + BOOST_CHECK_EQUAL( price_to_string( p[i][j], 0, 19 ), "0" ); + BOOST_CHECK_EQUAL( price_to_string( p[i][j], 19, 0 ), "0" ); + BOOST_CHECK_EQUAL( price_to_string( p[i][j], 19, 19 ), "0" ); + BOOST_CHECK_EQUAL( price_to_string( p[i][j], 20, 20 ), "0" ); + } + else + { + GRAPHENE_REQUIRE_THROW( price_to_string( p[i][j], 20, 0 ), fc::exception ); + GRAPHENE_REQUIRE_THROW( price_to_string( p[i][j], 0, 20 ), fc::exception ); + } + try { + idump( (i) (j) (p[i][j]) ); + idump( + (price_to_string(p[i][j],0,0)) + (price_to_string(p[i][j],0,1)) + (price_to_string(p[i][j],0,2)) + (price_to_string(p[i][j],0,8)) + (price_to_string(p[i][j],0,19)) + (price_to_string(p[i][j],1,0)) + (price_to_string(p[i][j],1,15)) + (price_to_string(p[i][j],2,6)) + (price_to_string(p[i][j],2,10)) + (price_to_string(p[i][j],5,0)) + (price_to_string(p[i][j],9,1)) + (price_to_string(p[i][j],9,9)) + (price_to_string(p[i][j],9,19)) + (price_to_string(p[i][j],18,10)) + (price_to_string(p[i][j],18,13)) + (price_to_string(p[i][j],18,19)) + (price_to_string(p[i][j],19,0)) + (price_to_string(p[i][j],19,7)) + (price_to_string(p[i][j],19,19)) + (price_diff_percent_string(p[i][j],p[j][i])) + ); + } catch(...) {} + } + +} + +BOOST_AUTO_TEST_SUITE_END()