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
186 IOUAmount const& amt,
187 std::uint32_t num,
188 std::uint32_t den,
189 bool roundUp)
190{
191 using namespace boost::multiprecision;
192
193 if (!den)
194 Throw<std::runtime_error>("division by zero");
195
196 // A vector with the value 10^index for indexes from 0 to 29
197 // The largest intermediate value we expect is 2^96, which
198 // is less than 10^29
199 static auto const powerTable = [] {
201 result.reserve(30); // 2^96 is largest intermediate result size
202 uint128_t cur(1);
203 for (int i = 0; i < 30; ++i)
204 {
205 result.push_back(cur);
206 cur *= 10;
207 };
208 return result;
209 }();
210
211 // Return floor(log10(v))
212 // Note: Returns -1 for v == 0
213 static auto log10Floor = [](uint128_t const& v) {
214 // Find the index of the first element >= the requested element, the
215 // index is the log of the element in the log table.
216 auto const l =
217 std::lower_bound(powerTable.begin(), powerTable.end(), v);
218 int index = std::distance(powerTable.begin(), l);
219 // If we're not equal, subtract to get the floor
220 if (*l != v)
221 --index;
222 return index;
223 };
224
225 // Return ceil(log10(v))
226 static auto log10Ceil = [](uint128_t const& v) {
227 // Find the index of the first element >= the requested element, the
228 // index is the log of the element in the log table.
229 auto const l =
230 std::lower_bound(powerTable.begin(), powerTable.end(), v);
231 return int(std::distance(powerTable.begin(), l));
232 };
233
234 static auto const fl64 =
236
237 bool const neg = amt.mantissa() < 0;
238 uint128_t const den128(den);
239 // a 32 value * a 64 bit value and stored in a 128 bit value. This will
240 // never overflow
241 uint128_t const mul =
242 uint128_t(neg ? -amt.mantissa() : amt.mantissa()) * uint128_t(num);
243
244 auto low = mul / den128;
245 uint128_t rem(mul - low * den128);
246
247 int exponent = amt.exponent();
248
249 if (rem)
250 {
251 // Mathematically, the result is low + rem/den128. However, since this
252 // uses integer division rem/den128 will be zero. Scale the result so
253 // low does not overflow the largest amount we can store in the mantissa
254 // and (rem/den128) is as large as possible. Scale by multiplying low
255 // and rem by 10 and subtracting one from the exponent. We could do this
256 // with a loop, but it's more efficient to use logarithms.
257 auto const roomToGrow = fl64 - log10Ceil(low);
258 if (roomToGrow > 0)
259 {
260 exponent -= roomToGrow;
261 low *= powerTable[roomToGrow];
262 rem *= powerTable[roomToGrow];
263 }
264 auto const addRem = rem / den128;
265 low += addRem;
266 rem = rem - addRem * den128;
267 }
268
269 // The largest result we can have is ~2^95, which overflows the 64 bit
270 // result we can store in the mantissa. Scale result down by dividing by ten
271 // and adding one to the exponent until the low will fit in the 64-bit
272 // mantissa. Use logarithms to avoid looping.
273 bool hasRem = bool(rem);
274 auto const mustShrink = log10Ceil(low) - fl64;
275 if (mustShrink > 0)
276 {
277 uint128_t const sav(low);
278 exponent += mustShrink;
279 low /= powerTable[mustShrink];
280 if (!hasRem)
281 hasRem = bool(sav - low * powerTable[mustShrink]);
282 }
283
284 std::int64_t mantissa = low.convert_to<std::int64_t>();
285
286 // normalize before rounding
287 if (neg)
288 mantissa *= -1;
289
290 IOUAmount result(mantissa, exponent);
291
292 if (hasRem)
293 {
294 // handle rounding
295 if (roundUp && !neg)
296 {
297 if (!result)
298 {
300 }
301 // This addition cannot overflow because the mantissa is already
302 // normalized
303 return IOUAmount(result.mantissa() + 1, result.exponent());
304 }
305
306 if (!roundUp && neg)
307 {
308 if (!result)
309 {
311 }
312 // This subtraction cannot underflow because `result` is not zero
313 return IOUAmount(result.mantissa() - 1, result.exponent());
314 }
315 }
316
317 return result;
318}
319
320} // namespace xrpl
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:27
IOUAmount & operator+=(IOUAmount const &other)
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:164
exponent_type exponent_
Definition IOUAmount.h:32
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:31
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:212
std::pair< T, int > normalizeToRange(T minMantissa, T maxMantissa) const
Definition Number.h:723
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:611
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)