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