xrpld
Loading...
Searching...
No Matches
STNumber_test.cpp
1#include <xrpl/basics/Number.h>
2#include <xrpl/beast/unit_test/suite.h>
3#include <xrpl/json/json_forwards.h>
4#include <xrpl/protocol/IOUAmount.h>
5#include <xrpl/protocol/Issue.h>
6#include <xrpl/protocol/SField.h>
7#include <xrpl/protocol/STAmount.h>
8#include <xrpl/protocol/STNumber.h>
9#include <xrpl/protocol/Serializer.h>
10
11#include <cstdint>
12#include <exception>
13#include <initializer_list>
14#include <limits>
15#include <sstream>
16#include <stdexcept>
17#include <string>
18#include <type_traits>
19#include <typeinfo>
20
21namespace xrpl {
22
24{
25 void
27 {
28 STNumber const before{sfNumber, number};
29 BEAST_EXPECT(number == before);
30 Serializer s;
31 before.add(s);
32 BEAST_EXPECT(s.size() == 12);
33 SerialIter sit(s.slice());
34 STNumber const after{sit, sfNumber};
35 BEAST_EXPECT(after.isEquivalent(before));
36 BEAST_EXPECT(number == after);
37 }
38
39 void
41 {
42 {
43 STNumber const stnum{sfNumber};
44 BEAST_EXPECT(stnum.getSType() == STI_NUMBER);
45 BEAST_EXPECT(stnum.getText() == "0");
46 BEAST_EXPECT(stnum.isDefault() == true);
47 BEAST_EXPECT(stnum.value() == Number{0});
48 }
49
50 std::initializer_list<std::int64_t> const mantissas = {
52 -1,
53 0,
54 1,
56 for (std::int64_t const mantissa : mantissas)
57 testCombo(Number{mantissa});
58
59 std::initializer_list<std::int32_t> const exponents = {
61 for (std::int32_t const exponent : exponents)
62 testCombo(Number{123, exponent});
63
64 {
65 STAmount const strikePrice{noIssue(), 100};
66 STNumber const factor{sfNumber, 100};
67 auto const iouValue = strikePrice.iou();
68 IOUAmount const totalValue{iouValue * factor};
69 STAmount const totalAmount{totalValue, strikePrice.get<Issue>()};
70 BEAST_EXPECT(totalAmount == Number{10'000});
71 }
72
73 {
74 BEAST_EXPECT(numberFromJson(sfNumber, json::Value(42)) == STNumber(sfNumber, 42));
75 BEAST_EXPECT(numberFromJson(sfNumber, json::Value(-42)) == STNumber(sfNumber, -42));
76
77 BEAST_EXPECT(numberFromJson(sfNumber, json::UInt(42)) == STNumber(sfNumber, 42));
78
79 BEAST_EXPECT(numberFromJson(sfNumber, "-123") == STNumber(sfNumber, -123));
80
81 BEAST_EXPECT(numberFromJson(sfNumber, "123") == STNumber(sfNumber, 123));
82 BEAST_EXPECT(numberFromJson(sfNumber, "-123") == STNumber(sfNumber, -123));
83
84 BEAST_EXPECT(numberFromJson(sfNumber, "3.14") == STNumber(sfNumber, Number(314, -2)));
85 BEAST_EXPECT(numberFromJson(sfNumber, "-3.14") == STNumber(sfNumber, -Number(314, -2)));
86 BEAST_EXPECT(numberFromJson(sfNumber, "3.14e2") == STNumber(sfNumber, 314));
87 BEAST_EXPECT(numberFromJson(sfNumber, "-3.14e2") == STNumber(sfNumber, -314));
88
89 BEAST_EXPECT(numberFromJson(sfNumber, "1000e-2") == STNumber(sfNumber, 10));
90 BEAST_EXPECT(numberFromJson(sfNumber, "-1000e-2") == STNumber(sfNumber, -10));
91
92 BEAST_EXPECT(numberFromJson(sfNumber, "0") == STNumber(sfNumber, 0));
93 BEAST_EXPECT(numberFromJson(sfNumber, "0.0") == STNumber(sfNumber, 0));
94 BEAST_EXPECT(numberFromJson(sfNumber, "0.000") == STNumber(sfNumber, 0));
95 BEAST_EXPECT(numberFromJson(sfNumber, "-0") == STNumber(sfNumber, 0));
96 BEAST_EXPECT(numberFromJson(sfNumber, "-0.0") == STNumber(sfNumber, 0));
97 BEAST_EXPECT(numberFromJson(sfNumber, "-0.000") == STNumber(sfNumber, 0));
98 BEAST_EXPECT(numberFromJson(sfNumber, "0e6") == STNumber(sfNumber, 0));
99 BEAST_EXPECT(numberFromJson(sfNumber, "0.0e6") == STNumber(sfNumber, 0));
100 BEAST_EXPECT(numberFromJson(sfNumber, "0.000e6") == STNumber(sfNumber, 0));
101 BEAST_EXPECT(numberFromJson(sfNumber, "-0e6") == STNumber(sfNumber, 0));
102 BEAST_EXPECT(numberFromJson(sfNumber, "-0.0e6") == STNumber(sfNumber, 0));
103 BEAST_EXPECT(numberFromJson(sfNumber, "-0.000e6") == STNumber(sfNumber, 0));
104
105 {
106 auto const parseNumber = [](std::string const& boundary) {
107 return numberFromJson(sfNumber, boundary);
108 };
109 auto const expectParseThrows = [this, &parseNumber](std::string const& boundary) {
110 try
111 {
112 parseNumber(boundary);
113 fail();
114 }
115 catch (std::exception const& e)
116 {
117 BEAST_EXPECT(std::string(e.what()) == "number cannot be represented");
118 }
119 };
120
121 // Small rejects this; large scales parse it as 9223372036854775800e-1.
122 auto constexpr positiveBoundary = "922337203685477580";
123 auto constexpr negativeBoundary = "-922337203685477580";
125 {
126 expectParseThrows(positiveBoundary);
127 expectParseThrows(negativeBoundary);
128 }
129 else
130 {
131 BEAST_EXPECT(
132 parseNumber(positiveBoundary) ==
133 STNumber(sfNumber, Number{922'337'203'685'477'580, 0}));
134 BEAST_EXPECT(
135 parseNumber(negativeBoundary) ==
136 STNumber(sfNumber, Number{-922'337'203'685'477'580, 0}));
137 }
138
140 // maxint64 9,223,372,036,854,775,807
142 // minint64 -9,223,372,036,854,775,808
145 {
146 // min/maxInt can't be exactly represented with the small mantissa, so they
147 // don't parse, and are expected to throw.
148 expectParseThrows(maxInt);
149 expectParseThrows(minInt);
150 }
151 else
152 {
153 // with large mantissas, maxint is fine
154 BEAST_EXPECT(
155 parseNumber(maxInt) ==
156 STNumber(sfNumber, Number{9'223'372'036'854'775'807, 0}));
157 // but minint's mantissa is > kMaxRep, and so rounds, and thus can't be parsed
158 expectParseThrows(minInt);
159 }
160 }
161
162 constexpr auto kIMin = std::numeric_limits<int>::min();
163 BEAST_EXPECT(numberFromJson(sfNumber, kIMin) == STNumber(sfNumber, Number(kIMin, 0)));
164 BEAST_EXPECT(
165 numberFromJson(sfNumber, std::to_string(kIMin)) ==
166 STNumber(sfNumber, Number(kIMin, 0)));
167
168 constexpr auto kIMax = std::numeric_limits<int>::max();
169 BEAST_EXPECT(numberFromJson(sfNumber, kIMax) == STNumber(sfNumber, Number(kIMax, 0)));
170 BEAST_EXPECT(
171 numberFromJson(sfNumber, std::to_string(kIMax)) ==
172 STNumber(sfNumber, Number(kIMax, 0)));
173
174 constexpr auto kUMax = std::numeric_limits<unsigned int>::max();
175 BEAST_EXPECT(numberFromJson(sfNumber, kUMax) == STNumber(sfNumber, Number(kUMax, 0)));
176 BEAST_EXPECT(
177 numberFromJson(sfNumber, std::to_string(kUMax)) ==
178 STNumber(sfNumber, Number(kUMax, 0)));
179
180 auto const expectJsonThrows = [this](
181 json::Value const& num, std::string const& expected) {
182 try
183 {
184 numberFromJson(sfNumber, num);
185 fail();
186 }
187 catch (std::exception const& e)
188 {
190 out << "Json: " << num.asString() << " got exception: " << e.what()
191 << ", expected: " << expected;
192 BEAST_EXPECTS(std::string(e.what()) == expected, out.str());
193 }
194 };
195
196 // Obvious overflows tested here
197 expectJsonThrows("1e2000000", "Number::normalize 2");
198 expectJsonThrows("1e2000000000", "Number::normalize 2");
199
200 // Obvious non-numbers tested here
201 expectJsonThrows("", "'' is not a number");
202 expectJsonThrows("e", "'e' is not a number");
203 expectJsonThrows("1e", "'1e' is not a number");
204 expectJsonThrows("e2", "'e2' is not a number");
205 expectJsonThrows(json::Value(), "not a number");
206
207 try
208 {
209 auto _ = numberFromJson(
210 sfNumber,
211 "1234567890123456789012345678901234567890123456789012345678"
212 "9012345678901234567890123456789012345678901234567890123456"
213 "78901234567890123456789012345678901234567890");
214 BEAST_EXPECT(false);
215 }
216 catch (std::bad_cast const& e)
217 {
218 BEAST_EXPECT(true);
219 }
220
221 // We do not handle leading zeros
222 try
223 {
224 auto _ = numberFromJson(sfNumber, "001");
225 BEAST_EXPECT(false);
226 }
227 catch (std::runtime_error const& e)
228 {
229 std::string const expected = "'001' is not a number";
230 BEAST_EXPECT(e.what() == expected);
231 }
232
233 try
234 {
235 auto _ = numberFromJson(sfNumber, "000.0");
236 BEAST_EXPECT(false);
237 }
238 catch (std::runtime_error const& e)
239 {
240 std::string const expected = "'000.0' is not a number";
241 BEAST_EXPECT(e.what() == expected);
242 }
243
244 // We do not handle dangling dot
245 try
246 {
247 auto _ = numberFromJson(sfNumber, ".1");
248 BEAST_EXPECT(false);
249 }
250 catch (std::runtime_error const& e)
251 {
252 std::string const expected = "'.1' is not a number";
253 BEAST_EXPECT(e.what() == expected);
254 }
255
256 try
257 {
258 auto _ = numberFromJson(sfNumber, "1.");
259 BEAST_EXPECT(false);
260 }
261 catch (std::runtime_error const& e)
262 {
263 std::string const expected = "'1.' is not a number";
264 BEAST_EXPECT(e.what() == expected);
265 }
266
267 try
268 {
269 auto _ = numberFromJson(sfNumber, "1.e3");
270 BEAST_EXPECT(false);
271 }
272 catch (std::runtime_error const& e)
273 {
274 std::string const expected = "'1.e3' is not a number";
275 BEAST_EXPECT(e.what() == expected);
276 }
277 }
278 }
279
280 void
281 run() override
282 {
284
285 for (auto const scale : MantissaRange::getAllScales())
286 {
289 doRun();
290 }
291 }
292};
293
295
296} // namespace xrpl
A testsuite class.
Definition suite.h:52
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:554
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Represents a JSON value.
Definition json_value.h:117
std::string asString() const
Returns the unquoted string value.
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:26
A currency issued by an account.
Definition Issue.h:18
Sets the new scale and restores the old scale when it leaves scope.
Definition Number.h:963
Number is a floating point type that can represent a wide range of values.
Definition Number.h:351
static constexpr int kMinExponent
Definition Number.h:361
static MantissaRange::MantissaScale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
static constexpr int kMaxExponent
Definition Number.h:362
constexpr TIss const & get() const
IOUAmount iou() const
Definition STAmount.cpp:287
A serializable number.
Definition STNumber.h:42
void add(Serializer &s) const override
Definition STNumber.cpp:70
bool isDefault() const override
Definition STNumber.cpp:148
std::string getText() const override
Definition STNumber.cpp:51
SerializedTypeID getSType() const override
Definition STNumber.cpp:45
Number const & value() const
Definition STNumber.cpp:115
Slice slice() const noexcept
Definition Serializer.h:141
std::size_t size() const noexcept
Definition Serializer.h:147
T is_convertible_v
T max(T... args)
T min(T... args)
unsigned int UInt
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
int scale(Number const &number, Asset const &asset)
Get the scale of a Number for a given asset.
Definition STAmount.h:794
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
bool after(NetClock::time_point now, std::uint32_t mark)
Has the specified time passed?
Definition View.cpp:644
STNumber numberFromJson(SField const &field, json::Value const &value)
Definition STNumber.cpp:220
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:118
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
T str(T... args)
static std::set< MantissaScale > const & getAllScales()
Definition Number.h:178
void testCombo(Number number)
void run() override
Runs the suite.
T to_string(T... args)
T what(T... args)