rippled
Loading...
Searching...
No Matches
IOUAmount.cpp
1#include <xrpl/protocol/IOUAmount.h>
2// Do not remove. Forces IOUAmount.h to stay first, to verify it can compile
3// without any hidden dependencies
4#include <xrpl/basics/LocalValue.h>
5#include <xrpl/basics/Number.h>
6#include <xrpl/basics/contract.h>
7#include <xrpl/beast/utility/Zero.h>
8#include <xrpl/protocol/STAmount.h>
9
10#include <boost/multiprecision/cpp_int.hpp>
11
12#include <algorithm>
13#include <cstdint>
14#include <iterator>
15#include <limits>
16#include <stdexcept>
17#include <string>
18#include <vector>
19
20namespace xrpl {
21
22namespace {
23
24// Use a static inside a function to help prevent order-of-initialization issues
25LocalValue<bool>&
26getStaticSTNumberSwitchover()
27{
28 static LocalValue<bool> r{true};
29 return r;
30}
31} // namespace
32
33bool
35{
36 return *getStaticSTNumberSwitchover();
37}
38
39void
41{
42 *getStaticSTNumberSwitchover() = v;
43}
44
45/* The range for the mantissa when normalized */
46// log(2^63,10) ~ 18.96
47//
50/* The range for the exponent when normalized */
51static int constexpr minExponent = STAmount::cMinOffset;
52static int constexpr maxExponent = STAmount::cMaxOffset;
53
56{
57 // Need to create a default IOUAmount and assign directly so it doesn't try
58 // to normalize, which calls fromNumber
59 IOUAmount result{};
60 std::tie(result.mantissa_, result.exponent_) = number.normalizeToRange(minMantissa, maxMantissa);
61 return result;
62}
63
69
70void
72{
73 if (mantissa_ == 0)
74 {
75 *this = beast::zero;
76 return;
77 }
78
80 {
81 Number const v{mantissa_, exponent_};
82 *this = fromNumber(v);
84 Throw<std::overflow_error>("value overflow");
86 *this = beast::zero;
87 return;
88 }
89
90 bool const negative = (mantissa_ < 0);
91
92 if (negative)
94
96 {
97 mantissa_ *= 10;
98 --exponent_;
99 }
100
101 while (mantissa_ > maxMantissa)
102 {
103 if (exponent_ >= maxExponent)
104 Throw<std::overflow_error>("IOUAmount::normalize");
105
106 mantissa_ /= 10;
107 ++exponent_;
108 }
109
111 {
112 *this = beast::zero;
113 return;
114 }
115
117 Throw<std::overflow_error>("value overflow");
118
119 if (negative)
121}
122
123IOUAmount::IOUAmount(Number const& other) : IOUAmount(fromNumber(other))
124{
126 Throw<std::overflow_error>("value overflow");
128 *this = beast::zero;
129}
130
133{
134 if (other == beast::zero)
135 return *this;
136
137 if (*this == beast::zero)
138 {
139 *this = other;
140 return *this;
141 }
142
144 {
145 *this = IOUAmount{Number{*this} + Number{other}};
146 return *this;
147 }
148 auto m = other.mantissa_;
149 auto e = other.exponent_;
150
151 while (exponent_ < e)
152 {
153 mantissa_ /= 10;
154 ++exponent_;
155 }
156
157 while (e < exponent_)
158 {
159 m /= 10;
160 ++e;
161 }
162
163 // This addition cannot overflow an std::int64_t but we may throw from
164 // normalize if the result isn't representable.
165 mantissa_ += m;
166
167 if (mantissa_ >= -10 && mantissa_ <= 10)
168 {
169 *this = beast::zero;
170 return *this;
171 }
172
173 normalize();
174 return *this;
175}
176
178to_string(IOUAmount const& amount)
179{
180 return to_string(Number{amount});
181}
182
183IOUAmount
184mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp)
185{
186 using namespace boost::multiprecision;
187
188 if (!den)
189 Throw<std::runtime_error>("division by zero");
190
191 // A vector with the value 10^index for indexes from 0 to 29
192 // The largest intermediate value we expect is 2^96, which
193 // is less than 10^29
194 static auto const powerTable = [] {
196 result.reserve(30); // 2^96 is largest intermediate result size
197 uint128_t cur(1);
198 for (int i = 0; i < 30; ++i)
199 {
200 result.push_back(cur);
201 cur *= 10;
202 };
203 return result;
204 }();
205
206 // Return floor(log10(v))
207 // Note: Returns -1 for v == 0
208 static auto log10Floor = [](uint128_t const& v) {
209 // Find the index of the first element >= the requested element, the
210 // index is the log of the element in the log table.
211 auto const l = std::lower_bound(powerTable.begin(), powerTable.end(), v);
212 int index = std::distance(powerTable.begin(), l);
213 // If we're not equal, subtract to get the floor
214 if (*l != v)
215 --index;
216 return index;
217 };
218
219 // Return ceil(log10(v))
220 static auto log10Ceil = [](uint128_t const& v) {
221 // Find the index of the first element >= the requested element, the
222 // index is the log of the element in the log table.
223 auto const l = std::lower_bound(powerTable.begin(), powerTable.end(), v);
224 return int(std::distance(powerTable.begin(), l));
225 };
226
227 static auto const fl64 = log10Floor(std::numeric_limits<std::int64_t>::max());
228
229 bool const neg = amt.mantissa() < 0;
230 uint128_t const den128(den);
231 // a 32 value * a 64 bit value and stored in a 128 bit value. This will
232 // never overflow
233 uint128_t const mul = uint128_t(neg ? -amt.mantissa() : amt.mantissa()) * uint128_t(num);
234
235 auto low = mul / den128;
236 uint128_t rem(mul - low * den128);
237
238 int exponent = amt.exponent();
239
240 if (rem)
241 {
242 // Mathematically, the result is low + rem/den128. However, since this
243 // uses integer division rem/den128 will be zero. Scale the result so
244 // low does not overflow the largest amount we can store in the mantissa
245 // and (rem/den128) is as large as possible. Scale by multiplying low
246 // and rem by 10 and subtracting one from the exponent. We could do this
247 // with a loop, but it's more efficient to use logarithms.
248 auto const roomToGrow = fl64 - log10Ceil(low);
249 if (roomToGrow > 0)
250 {
251 exponent -= roomToGrow;
252 low *= powerTable[roomToGrow];
253 rem *= powerTable[roomToGrow];
254 }
255 auto const addRem = rem / den128;
256 low += addRem;
257 rem = rem - addRem * den128;
258 }
259
260 // The largest result we can have is ~2^95, which overflows the 64 bit
261 // result we can store in the mantissa. Scale result down by dividing by ten
262 // and adding one to the exponent until the low will fit in the 64-bit
263 // mantissa. Use logarithms to avoid looping.
264 bool hasRem = bool(rem);
265 auto const mustShrink = log10Ceil(low) - fl64;
266 if (mustShrink > 0)
267 {
268 uint128_t const sav(low);
269 exponent += mustShrink;
270 low /= powerTable[mustShrink];
271 if (!hasRem)
272 hasRem = bool(sav - low * powerTable[mustShrink]);
273 }
274
275 std::int64_t mantissa = low.convert_to<std::int64_t>();
276
277 // normalize before rounding
278 if (neg)
279 mantissa *= -1;
280
281 IOUAmount result(mantissa, exponent);
282
283 if (hasRem)
284 {
285 // handle rounding
286 if (roundUp && !neg)
287 {
288 if (!result)
289 {
291 }
292 // This addition cannot overflow because the mantissa is already
293 // normalized
294 return IOUAmount(result.mantissa() + 1, result.exponent());
295 }
296
297 if (!roundUp && neg)
298 {
299 if (!result)
300 {
302 }
303 // This subtraction cannot underflow because `result` is not zero
304 return IOUAmount(result.mantissa() - 1, result.exponent());
305 }
306 }
307
308 return result;
309}
310
311} // namespace xrpl
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:26
IOUAmount & operator+=(IOUAmount const &other)
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:162
exponent_type exponent_
Definition IOUAmount.h:31
IOUAmount()=default
exponent_type exponent() const noexcept
Definition IOUAmount.h:156
static IOUAmount fromNumber(Number const &number)
Definition IOUAmount.cpp:55
mantissa_type mantissa_
Definition IOUAmount.h:30
static IOUAmount minPositiveAmount()
Definition IOUAmount.cpp:65
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition IOUAmount.cpp:71
Number is a floating point type that can represent a wide range of values.
Definition Number.h:208
std::pair< T, int > normalizeToRange(T minMantissa, T maxMantissa) const
Definition Number.h:692
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:52
static int const cMaxOffset
Definition STAmount.h:47
static constexpr std::uint64_t cMinValue
Definition STAmount.h:50
static int const cMinOffset
Definition STAmount.h:46
T distance(T... args)
T lower_bound(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
static std::int64_t constexpr minMantissa
Definition IOUAmount.cpp:48
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:598
void setSTNumberSwitchover(bool v)
Definition IOUAmount.cpp:40
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
static std::int64_t constexpr maxMantissa
Definition IOUAmount.cpp:49
static int constexpr maxExponent
Definition IOUAmount.cpp:52
static int constexpr minExponent
Definition IOUAmount.cpp:51
bool getSTNumberSwitchover()
Definition IOUAmount.cpp:34
T push_back(T... args)
T reserve(T... args)
T tie(T... args)