Skip to content

Commit bfd807f

Browse files
committed
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)
2 parents 410fd74 + ec249d4 commit bfd807f

File tree

8 files changed

+44
-24
lines changed

8 files changed

+44
-24
lines changed

qa/rpc-tests/rest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import binascii
1515
import json
1616
import StringIO
17+
import decimal
1718

1819
try:
1920
import http.client as httplib
@@ -243,7 +244,7 @@ def run_test(self):
243244
response_header_json = http_get_call(url.hostname, url.port, '/rest/headers/1/'+bb_hash+self.FORMAT_SEPARATOR+"json", "", True)
244245
assert_equal(response_header_json.status, 200)
245246
response_header_json_str = response_header_json.read()
246-
json_obj = json.loads(response_header_json_str)
247+
json_obj = json.loads(response_header_json_str, parse_float=decimal.Decimal)
247248
assert_equal(len(json_obj), 1) #ensure that there is one header in the json response
248249
assert_equal(json_obj[0]['hash'], bb_hash) #request/response hash should be the same
249250

src/rpcserver.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
#include "sync.h"
1212
#include "ui_interface.h"
1313
#include "util.h"
14-
#include "utilmoneystr.h"
1514
#include "utilstrencodings.h"
1615
#ifdef ENABLE_WALLET
1716
#include "wallet/wallet.h"
@@ -121,7 +120,7 @@ void RPCTypeCheckObj(const UniValue& o,
121120

122121
CAmount AmountFromValue(const UniValue& value)
123122
{
124-
if (!value.isReal() && !value.isNum())
123+
if (!value.isNum())
125124
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number");
126125
CAmount amount;
127126
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
@@ -133,7 +132,12 @@ CAmount AmountFromValue(const UniValue& value)
133132

134133
UniValue ValueFromAmount(const CAmount& amount)
135134
{
136-
return UniValue(UniValue::VREAL, FormatMoney(amount));
135+
bool sign = amount < 0;
136+
int64_t n_abs = (sign ? -amount : amount);
137+
int64_t quotient = n_abs / COIN;
138+
int64_t remainder = n_abs % COIN;
139+
return UniValue(UniValue::VNUM,
140+
strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
137141
}
138142

139143
uint256 ParseHashV(const UniValue& v, string strName)

src/test/rpc_tests.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,29 @@ BOOST_AUTO_TEST_CASE(rpc_format_monetary_values)
120120
BOOST_CHECK(ValueFromAmount(100000000LL).write() == "1.00000000");
121121
BOOST_CHECK(ValueFromAmount(2099999999999990LL).write() == "20999999.99999990");
122122
BOOST_CHECK(ValueFromAmount(2099999999999999LL).write() == "20999999.99999999");
123+
124+
BOOST_CHECK_EQUAL(ValueFromAmount(0).write(), "0.00000000");
125+
BOOST_CHECK_EQUAL(ValueFromAmount((COIN/10000)*123456789).write(), "12345.67890000");
126+
BOOST_CHECK_EQUAL(ValueFromAmount(-COIN).write(), "-1.00000000");
127+
BOOST_CHECK_EQUAL(ValueFromAmount(-COIN/10).write(), "-0.10000000");
128+
129+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100000000).write(), "100000000.00000000");
130+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10000000).write(), "10000000.00000000");
131+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*1000000).write(), "1000000.00000000");
132+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100000).write(), "100000.00000000");
133+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10000).write(), "10000.00000000");
134+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*1000).write(), "1000.00000000");
135+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*100).write(), "100.00000000");
136+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN*10).write(), "10.00000000");
137+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN).write(), "1.00000000");
138+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10).write(), "0.10000000");
139+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100).write(), "0.01000000");
140+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000).write(), "0.00100000");
141+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000).write(), "0.00010000");
142+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000).write(), "0.00001000");
143+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/1000000).write(), "0.00000100");
144+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/10000000).write(), "0.00000010");
145+
BOOST_CHECK_EQUAL(ValueFromAmount(COIN/100000000).write(), "0.00000001");
123146
}
124147

125148
static UniValue ValueFromString(const std::string &str)

src/test/univalue_tests.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(univalue_constructor)
4949

5050
double vd = -7.21;
5151
UniValue v7(vd);
52-
BOOST_CHECK(v7.isReal());
52+
BOOST_CHECK(v7.isNum());
5353
BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21");
5454

5555
string vs("yawn");
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(univalue_set)
127127
BOOST_CHECK_EQUAL(v.getValStr(), "zum");
128128

129129
BOOST_CHECK(v.setFloat(-1.01));
130-
BOOST_CHECK(v.isReal());
130+
BOOST_CHECK(v.isNum());
131131
BOOST_CHECK_EQUAL(v.getValStr(), "-1.01");
132132

133133
BOOST_CHECK(v.setInt((int)1023));
@@ -272,7 +272,7 @@ BOOST_AUTO_TEST_CASE(univalue_object)
272272
objTypes["distance"] = UniValue::VNUM;
273273
objTypes["time"] = UniValue::VNUM;
274274
objTypes["calories"] = UniValue::VNUM;
275-
objTypes["temperature"] = UniValue::VREAL;
275+
objTypes["temperature"] = UniValue::VNUM;
276276
objTypes["cat1"] = UniValue::VNUM;
277277
objTypes["cat2"] = UniValue::VNUM;
278278
BOOST_CHECK(obj.checkObject(objTypes));

src/univalue/univalue.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ bool UniValue::setFloat(double val)
8686
oss << std::setprecision(16) << val;
8787

8888
bool ret = setNumStr(oss.str());
89-
typ = VREAL;
89+
typ = VNUM;
9090
return ret;
9191
}
9292

@@ -210,7 +210,6 @@ const char *uvTypeName(UniValue::VType t)
210210
case UniValue::VARR: return "array";
211211
case UniValue::VSTR: return "string";
212212
case UniValue::VNUM: return "number";
213-
case UniValue::VREAL: return "number";
214213
}
215214

216215
// not reached
@@ -280,7 +279,7 @@ int64_t UniValue::get_int64() const
280279

281280
double UniValue::get_real() const
282281
{
283-
if (typ != VREAL && typ != VNUM)
282+
if (typ != VNUM)
284283
throw std::runtime_error("JSON value is not a number as expected");
285284
double retval;
286285
if (!ParseDouble(getValStr(), &retval))

src/univalue/univalue.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
class UniValue {
1818
public:
19-
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VREAL, VBOOL, };
19+
enum VType { VNULL, VOBJ, VARR, VSTR, VNUM, VBOOL, };
2020

2121
UniValue() { typ = VNULL; }
2222
UniValue(UniValue::VType initialType, const std::string& initialStr = "") {
@@ -78,7 +78,6 @@ class UniValue {
7878
bool isBool() const { return (typ == VBOOL); }
7979
bool isStr() const { return (typ == VSTR); }
8080
bool isNum() const { return (typ == VNUM); }
81-
bool isReal() const { return (typ == VREAL); }
8281
bool isArray() const { return (typ == VARR); }
8382
bool isObject() const { return (typ == VOBJ); }
8483

src/univalue/univalue_write.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ string UniValue::write(unsigned int prettyIndent,
6161
case VSTR:
6262
s += "\"" + json_escape(val) + "\"";
6363
break;
64-
case VREAL:
65-
{
66-
std::stringstream ss;
67-
ss << std::showpoint << std::fixed << std::setprecision(8) << get_real();
68-
s += ss.str();
69-
}
70-
break;
7164
case VNUM:
7265
s += val;
7366
break;

src/utilstrencodings.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,12 @@ bool ParseDouble(const std::string& str, double *out)
464464
return false;
465465
if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed
466466
return false;
467-
char *endp = NULL;
468-
errno = 0; // strtod will not set errno if valid
469-
double n = strtod(str.c_str(), &endp);
470-
if(out) *out = n;
471-
return endp && *endp == 0 && !errno;
467+
std::istringstream text(str);
468+
text.imbue(std::locale::classic());
469+
double result;
470+
text >> result;
471+
if(out) *out = result;
472+
return text.eof() && !text.fail();
472473
}
473474

474475
std::string FormatParagraph(const std::string& in, size_t width, size_t indent)

0 commit comments

Comments
 (0)