Merge pull request #6456

ec249d4 util: use locale-independent parsing in ParseDouble (Wladimir J. van der Laan)
7650449 univalue: Avoid unnecessary roundtrip through double for numbers (Wladimir J. van der Laan)
e061e27 rpc: Make ValueFromAmount always return 8 decimals (Wladimir J. van der Laan)
This commit is contained in:
Wladimir J. van der Laan 2015-07-24 11:07:52 +02:00
commit bfd807ff32
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
8 changed files with 44 additions and 24 deletions

View File

@ -14,6 +14,7 @@ from struct import *
import binascii import binascii
import json import json
import StringIO import StringIO
import decimal
try: try:
import http.client as httplib import http.client as httplib
@ -243,7 +244,7 @@ class RESTTest (BitcoinTestFramework):
response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True) response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True)
assert_equal(response_header_json.status, 200) assert_equal(response_header_json.status, 200)
response_header_json_str = response_header_json.read() response_header_json_str = response_header_json.read()
json_obj = json.loads(response_header_json_str) json_obj = json.loads(response_header_json_str, parse_float=decimal.Decimal)
assert_equal(len(json_obj), 1) #ensure that there is one header in the json response assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same

View File

@ -11,7 +11,6 @@
#include "sync.h" #include "sync.h"
#include "ui_interface.h" #include "ui_interface.h"
#include "util.h" #include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h" #include "utilstrencodings.h"
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
#include "wallet/wallet.h" #include "wallet/wallet.h"
@ -121,7 +120,7 @@ void RPCTypeCheckObj(const UniValue& o,
CAmount AmountFromValue(const UniValue& value) CAmount AmountFromValue(const UniValue& value)
{ {
if (!value.isReal() && !value.isNum()) if (!value.isNum())
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number"); throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number");
CAmount amount; CAmount amount;
if (!ParseFixedPoint(value.getValStr(), 8, &amount)) if (!ParseFixedPoint(value.getValStr(), 8, &amount))
@ -133,7 +132,12 @@ CAmount AmountFromValue(const UniValue& value)
UniValue ValueFromAmount(const CAmount& amount) UniValue ValueFromAmount(const CAmount& amount)
{ {
return UniValue(UniValue::VREAL, FormatMoney(amount)); bool sign = amount < 0;
int64_t n_abs = (sign ? -amount : amount);
int64_t quotient = n_abs / COIN;
int64_t remainder = n_abs % COIN;
return UniValue(UniValue::VNUM,
strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
} }
uint256 ParseHashV(const UniValue& v, string strName) uint256 ParseHashV(const UniValue& v, string strName)

View File

@ -120,6 +120,29 @@ BOOST_AUTO_TEST_CASE(rpc_format_monetary_values)
BOOST_CHECK(ValueFromAmount(100000000LL).write() == "1.00000000"); BOOST_CHECK(ValueFromAmount(100000000LL).write() == "1.00000000");
BOOST_CHECK(ValueFromAmount(2099999999999990LL).write() == "20999999.99999990"); BOOST_CHECK(ValueFromAmount(2099999999999990LL).write() == "20999999.99999990");
BOOST_CHECK(ValueFromAmount(2099999999999999LL).write() == "20999999.99999999"); BOOST_CHECK(ValueFromAmount(2099999999999999LL).write() == "20999999.99999999");
BOOST_CHECK_EQUAL(ValueFromAmount(0).write(), "0.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount((COIN/10000)*123456789).write(), "12345.67890000");
BOOST_CHECK_EQUAL(ValueFromAmount(-COIN).write(), "-1.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(-COIN/10).write(), "-0.10000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100000000).write(), "100000000.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10000000).write(), "10000000.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*1000000).write(), "1000000.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100000).write(), "100000.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10000).write(), "10000.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*1000).write(), "1000.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100).write(), "100.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10).write(), "10.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN).write(), "1.00000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10).write(), "0.10000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100).write(), "0.01000000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000).write(), "0.00100000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000).write(), "0.00010000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000).write(), "0.00001000");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000000).write(), "0.00000100");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000000).write(), "0.00000010");
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000000).write(), "0.00000001");
} }
static UniValue ValueFromString(const std::string &str) static UniValue ValueFromString(const std::string &str)

View File

@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(univalue_constructor)
double vd = -7.21; double vd = -7.21;
UniValue v7(vd); UniValue v7(vd);
BOOST_CHECK(v7.isReal()); BOOST_CHECK(v7.isNum());
BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21");
string vs("yawn"); string vs("yawn");
@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(univalue_set)
BOOST_CHECK_EQUAL(v.getValStr(), "zum"); BOOST_CHECK_EQUAL(v.getValStr(), "zum");
BOOST_CHECK(v.setFloat(-1.01)); BOOST_CHECK(v.setFloat(-1.01));
BOOST_CHECK(v.isReal()); BOOST_CHECK(v.isNum());
BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); BOOST_CHECK_EQUAL(v.getValStr(), "-1.01");
BOOST_CHECK(v.setInt((int)1023)); BOOST_CHECK(v.setInt((int)1023));
@ -272,7 +272,7 @@ BOOST_AUTO_TEST_CASE(univalue_object)
objTypes["distance"] = UniValue::VNUM; objTypes["distance"] = UniValue::VNUM;
objTypes["time"] = UniValue::VNUM; objTypes["time"] = UniValue::VNUM;
objTypes["calories"] = UniValue::VNUM; objTypes["calories"] = UniValue::VNUM;
objTypes["temperature"] = UniValue::VREAL; objTypes["temperature"] = UniValue::VNUM;
objTypes["cat1"] = UniValue::VNUM; objTypes["cat1"] = UniValue::VNUM;
objTypes["cat2"] = UniValue::VNUM; objTypes["cat2"] = UniValue::VNUM;
BOOST_CHECK(obj.checkObject(objTypes)); BOOST_CHECK(obj.checkObject(objTypes));

View File

@ -86,7 +86,7 @@ bool UniValue::setFloat(double val)
oss << std::setprecision(16) << val; oss << std::setprecision(16) << val;
bool ret = setNumStr(oss.str()); bool ret = setNumStr(oss.str());
typ = VREAL; typ = VNUM;
return ret; return ret;
} }
@ -210,7 +210,6 @@ const char *uvTypeName(UniValue::VType t)
case UniValue::VARR: return "array"; case UniValue::VARR: return "array";
case UniValue::VSTR: return "string"; case UniValue::VSTR: return "string";
case UniValue::VNUM: return "number"; case UniValue::VNUM: return "number";
case UniValue::VREAL: return "number";
} }
// not reached // not reached
@ -280,7 +279,7 @@ int64_t UniValue::get_int64() const
double UniValue::get_real() const double UniValue::get_real() const
{ {
if (typ != VREAL && typ != VNUM) if (typ != VNUM)
throw std::runtime_error("JSON value is not a number as expected"); throw std::runtime_error("JSON value is not a number as expected");
double retval; double retval;
if (!ParseDouble(getValStr(), &retval)) if (!ParseDouble(getValStr(), &retval))

View File

@ -16,7 +16,7 @@
class UniValue { class UniValue {
public: public:
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VREAL, VBOOL, }; enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
UniValue() { typ = VNULL; } UniValue() { typ = VNULL; }
UniValue(UniValue::VType initialType, const std::string& initialStr = "") { UniValue(UniValue::VType initialType, const std::string& initialStr = "") {
@ -78,7 +78,6 @@ public:
bool isBool() const { return (typ == VBOOL); } bool isBool() const { return (typ == VBOOL); }
bool isStr() const { return (typ == VSTR); } bool isStr() const { return (typ == VSTR); }
bool isNum() const { return (typ == VNUM); } bool isNum() const { return (typ == VNUM); }
bool isReal() const { return (typ == VREAL); }
bool isArray() const { return (typ == VARR); } bool isArray() const { return (typ == VARR); }
bool isObject() const { return (typ == VOBJ); } bool isObject() const { return (typ == VOBJ); }

View File

@ -61,13 +61,6 @@ string UniValue::write(unsigned int prettyIndent,
case VSTR: case VSTR:
s += "\"" + json_escape(val) + "\""; s += "\"" + json_escape(val) + "\"";
break; break;
case VREAL:
{
std::stringstream ss;
ss << std::showpoint << std::fixed << std::setprecision(8) << get_real();
s += ss.str();
}
break;
case VNUM: case VNUM:
s += val; s += val;
break; break;

View File

@ -464,11 +464,12 @@ bool ParseDouble(const std::string& str, double *out)
return false; return false;
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
return false; return false;
char *endp = NULL; std::istringstream text(str);
errno = 0; // strtod will not set errno if valid text.imbue(std::locale::classic());
double n = strtod(str.c_str(), &endp); double result;
if(out) *out = n; text >> result;
return endp && *endp == 0 && !errno; if(out) *out = result;
return text.eof() && !text.fail();
} }
std::string FormatParagraph(const std::string& in, size_t width, size_t indent) std::string FormatParagraph(const std::string& in, size_t width, size_t indent)