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_) =
62 return result;
63}
64
70
71void
73{
74 if (mantissa_ == 0)
75 {
76 *this = beast::zero;
77 return;
78 }
79
81 {
82 Number const v{mantissa_, exponent_};
83 *this = fromNumber(v);
85 Throw<std::overflow_error>("value overflow");
87 *this = beast::zero;
88 return;
89 }
90
91 bool const negative = (mantissa_ < 0);
92
93 if (negative)
95
97 {
98 mantissa_ *= 10;
99 --exponent_;
100 }
101
102 while (mantissa_ > maxMantissa)
103 {
104 if (exponent_ >= maxExponent)
105 Throw<std::overflow_error>("IOUAmount::normalize");
106
107 mantissa_ /= 10;
108 ++exponent_;
109 }
110
112 {
113 *this = beast::zero;
114 return;
115 }
116
118 Throw<std::overflow_error>("value overflow");
119
120 if (negative)
122}
123
124IOUAmount::IOUAmount(Number const& other) : IOUAmount(fromNumber(other))
125{
127 Throw<std::overflow_error>("value overflow");
129 *this = beast::zero;
130}
131
134{
135 if (other == beast::zero)
136 return *this;
137
138 if (*this == beast::zero)
139 {
140 *this = other;
141 return *this;
142 }
143
145 {
146 *this = IOUAmount{Number{*this} + Number{other}};
147 return *this;
148 }
149 auto m = other.mantissa_;
150 auto e = other.exponent_;
151
152 while (exponent_ < e)
153 {
154 mantissa_ /= 10;
155 ++exponent_;
156 }
157
158 while (e < exponent_)
159 {
160 m /= 10;
161 ++e;
162 }
163
164 // This addition cannot overflow an std::int64_t but we may throw from
165 // normalize if the result isn't representable.
166 mantissa_ += m;
167
168 if (mantissa_ >= -10 && mantissa_ <= 10)
169 {
170 *this = beast::zero;
171 return *this;
172 }
173
174 normalize();
175 return *this;
176}
177
179to_string(IOUAmount const& amount)
180{
181 return to_string(Number{amount});
182}
183
184IOUAmount
185mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp)
186{
187 using namespace boost::multiprecision;
188
189 if (den == 0u)
190 Throw<std::runtime_error>("division by zero");
191
192 // A vector with the value 10^index for indexes from 0 to 29
193 // The largest intermediate value we expect is 2^96, which
194 // is less than 10^29
195 static auto const powerTable = [] {
197 result.reserve(30); // 2^96 is largest intermediate result size
198 uint128_t cur(1);
199 for (int i = 0; i < 30; ++i)
200 {
201 result.push_back(cur);
202 cur *= 10;
203 };
204 return result;
205 }();
206
207 // Return floor(log10(v))
208 // Note: Returns -1 for v == 0
209 static auto log10Floor = [](uint128_t const& v) {
210 // Find the index of the first element >= the requested element, the
211 // index is the log of the element in the log table.
212 auto const l = std::lower_bound(powerTable.begin(), powerTable.end(), v);
213 int index = std::distance(powerTable.begin(), l);
214 // If we're not equal, subtract to get the floor
215 if (*l != v)
216 --index;
217 return index;
218 };
219
220 // Return ceil(log10(v))
221 static auto log10Ceil = [](uint128_t const& v) {
222 // Find the index of the first element >= the requested element, the
223 // index is the log of the element in the log table.
224 auto const l = std::lower_bound(powerTable.begin(), powerTable.end(), v);
225 return int(std::distance(powerTable.begin(), l));
226 };
227
228 static auto const fl64 = log10Floor(std::numeric_limits<std::int64_t>::max());
229
230 bool const neg = amt.mantissa() < 0;
231 uint128_t const den128(den);
232 // a 32 value * a 64 bit value and stored in a 128 bit value. This will
233 // never overflow
234 uint128_t const mul = uint128_t(neg ? -amt.mantissa() : amt.mantissa()) * uint128_t(num);
235
236 auto low = mul / den128;
237 uint128_t rem(mul - low * den128);
238
239 int exponent = amt.exponent();
240
241 if (rem)
242 {
243 // Mathematically, the result is low + rem/den128. However, since this
244 // uses integer division rem/den128 will be zero. Scale the result so
245 // low does not overflow the largest amount we can store in the mantissa
246 // and (rem/den128) is as large as possible. Scale by multiplying low
247 // and rem by 10 and subtracting one from the exponent. We could do this
248 // with a loop, but it's more efficient to use logarithms.
249 auto const roomToGrow = fl64 - log10Ceil(low);
250 if (roomToGrow > 0)
251 {
252 exponent -= roomToGrow;
253 low *= powerTable[roomToGrow];
254 rem *= powerTable[roomToGrow];
255 }
256 auto const addRem = rem / den128;
257 low += addRem;
258 rem = rem - addRem * den128;
259 }
260
261 // The largest result we can have is ~2^95, which overflows the 64 bit
262 // result we can store in the mantissa. Scale result down by dividing by ten
263 // and adding one to the exponent until the low will fit in the 64-bit
264 // mantissa. Use logarithms to avoid looping.
265 bool hasRem = bool(rem);
266 auto const mustShrink = log10Ceil(low) - fl64;
267 if (mustShrink > 0)
268 {
269 uint128_t const sav(low);
270 exponent += mustShrink;
271 low /= powerTable[mustShrink];
272 if (!hasRem)
273 hasRem = bool(sav - low * powerTable[mustShrink]);
274 }
275
276 std::int64_t mantissa = low.convert_to<std::int64_t>();
277
278 // normalize before rounding
279 if (neg)
280 mantissa *= -1;
281
282 IOUAmount result(mantissa, exponent);
283
284 if (hasRem)
285 {
286 // handle rounding
287 if (roundUp && !neg)
288 {
289 if (!result)
290 {
292 }
293 // This addition cannot overflow because the mantissa is already
294 // normalized
295 return IOUAmount(result.mantissa() + 1, result.exponent());
296 }
297
298 if (!roundUp && neg)
299 {
300 if (!result)
301 {
303 }
304 // This subtraction cannot underflow because `result` is not zero
305 return IOUAmount(result.mantissa() - 1, result.exponent());
306 }
307 }
308
309 return result;
310}
311
312} // namespace xrpl
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
IOUAmount & operator+=(IOUAmount const &other)
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:164
exponent_type exponent_
Definition IOUAmount.h:30
IOUAmount()=default
exponent_type exponent() const noexcept
Definition IOUAmount.h:158
static IOUAmount fromNumber(Number const &number)
Definition IOUAmount.cpp:55
mantissa_type mantissa_
Definition IOUAmount.h:29
static IOUAmount minPositiveAmount()
Definition IOUAmount.cpp:66
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition IOUAmount.cpp:72
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
std::pair< T, int > normalizeToRange(T minMantissa, T maxMantissa) const
Definition Number.h:701
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
static int const cMaxOffset
Definition STAmount.h:46
static constexpr std::uint64_t cMinValue
Definition STAmount.h:49
static int const cMinOffset
Definition STAmount.h:45
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:5
static std::int64_t constexpr minMantissa
Definition IOUAmount.cpp:48
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
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)