rippled
Loading...
Searching...
No Matches
IOUAmount.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpl/basics/LocalValue.h>
21#include <xrpl/basics/Number.h>
22#include <xrpl/basics/contract.h>
23#include <xrpl/beast/utility/Zero.h>
24#include <xrpl/protocol/IOUAmount.h>
25
26#include <boost/multiprecision/cpp_int.hpp>
27
28#include <algorithm>
29#include <cstdint>
30#include <iterator>
31#include <limits>
32#include <stdexcept>
33#include <string>
34#include <vector>
35
36namespace ripple {
37
38namespace {
39
40// Use a static inside a function to help prevent order-of-initialzation issues
41LocalValue<bool>&
42getStaticSTNumberSwitchover()
43{
44 static LocalValue<bool> r{true};
45 return r;
46}
47} // namespace
48
49bool
51{
52 return *getStaticSTNumberSwitchover();
53}
54
55void
57{
58 *getStaticSTNumberSwitchover() = v;
59}
60
61/* The range for the mantissa when normalized */
62static std::int64_t constexpr minMantissa = 1000000000000000ull;
63static std::int64_t constexpr maxMantissa = 9999999999999999ull;
64/* The range for the exponent when normalized */
65static int constexpr minExponent = -96;
66static int constexpr maxExponent = 80;
67
73
74void
76{
77 if (mantissa_ == 0)
78 {
79 *this = beast::zero;
80 return;
81 }
82
84 {
85 Number const v{mantissa_, exponent_};
86 mantissa_ = v.mantissa();
87 exponent_ = v.exponent();
89 Throw<std::overflow_error>("value overflow");
91 *this = beast::zero;
92 return;
93 }
94
95 bool const negative = (mantissa_ < 0);
96
97 if (negative)
99
100 while ((mantissa_ < minMantissa) && (exponent_ > minExponent))
101 {
102 mantissa_ *= 10;
103 --exponent_;
104 }
105
106 while (mantissa_ > maxMantissa)
107 {
108 if (exponent_ >= maxExponent)
109 Throw<std::overflow_error>("IOUAmount::normalize");
110
111 mantissa_ /= 10;
112 ++exponent_;
113 }
114
116 {
117 *this = beast::zero;
118 return;
119 }
120
122 Throw<std::overflow_error>("value overflow");
123
124 if (negative)
126}
127
129 : mantissa_(other.mantissa()), exponent_(other.exponent())
130{
132 Throw<std::overflow_error>("value overflow");
134 *this = beast::zero;
135}
136
139{
140 if (other == beast::zero)
141 return *this;
142
143 if (*this == beast::zero)
144 {
145 *this = other;
146 return *this;
147 }
148
150 {
151 *this = IOUAmount{Number{*this} + Number{other}};
152 return *this;
153 }
154 auto m = other.mantissa_;
155 auto e = other.exponent_;
156
157 while (exponent_ < e)
158 {
159 mantissa_ /= 10;
160 ++exponent_;
161 }
162
163 while (e < exponent_)
164 {
165 m /= 10;
166 ++e;
167 }
168
169 // This addition cannot overflow an std::int64_t but we may throw from
170 // normalize if the result isn't representable.
171 mantissa_ += m;
172
173 if (mantissa_ >= -10 && mantissa_ <= 10)
174 {
175 *this = beast::zero;
176 return *this;
177 }
178
179 normalize();
180 return *this;
181}
182
184to_string(IOUAmount const& amount)
185{
186 return to_string(Number{amount});
187}
188
189IOUAmount
191 IOUAmount const& amt,
192 std::uint32_t num,
193 std::uint32_t den,
194 bool roundUp)
195{
196 using namespace boost::multiprecision;
197
198 if (!den)
199 Throw<std::runtime_error>("division by zero");
200
201 // A vector with the value 10^index for indexes from 0 to 29
202 // The largest intermediate value we expect is 2^96, which
203 // is less than 10^29
204 static auto const powerTable = [] {
206 result.reserve(30); // 2^96 is largest intermediate result size
207 uint128_t cur(1);
208 for (int i = 0; i < 30; ++i)
209 {
210 result.push_back(cur);
211 cur *= 10;
212 };
213 return result;
214 }();
215
216 // Return floor(log10(v))
217 // Note: Returns -1 for v == 0
218 static auto log10Floor = [](uint128_t const& v) {
219 // Find the index of the first element >= the requested element, the
220 // index is the log of the element in the log table.
221 auto const l =
222 std::lower_bound(powerTable.begin(), powerTable.end(), v);
223 int index = std::distance(powerTable.begin(), l);
224 // If we're not equal, subtract to get the floor
225 if (*l != v)
226 --index;
227 return index;
228 };
229
230 // Return ceil(log10(v))
231 static auto log10Ceil = [](uint128_t const& v) {
232 // Find the index of the first element >= the requested element, the
233 // index is the log of the element in the log table.
234 auto const l =
235 std::lower_bound(powerTable.begin(), powerTable.end(), v);
236 return int(std::distance(powerTable.begin(), l));
237 };
238
239 static auto const fl64 =
241
242 bool const neg = amt.mantissa() < 0;
243 uint128_t const den128(den);
244 // a 32 value * a 64 bit value and stored in a 128 bit value. This will
245 // never overflow
246 uint128_t const mul =
247 uint128_t(neg ? -amt.mantissa() : amt.mantissa()) * uint128_t(num);
248
249 auto low = mul / den128;
250 uint128_t rem(mul - low * den128);
251
252 int exponent = amt.exponent();
253
254 if (rem)
255 {
256 // Mathematically, the result is low + rem/den128. However, since this
257 // uses integer division rem/den128 will be zero. Scale the result so
258 // low does not overflow the largest amount we can store in the mantissa
259 // and (rem/den128) is as large as possible. Scale by multiplying low
260 // and rem by 10 and subtracting one from the exponent. We could do this
261 // with a loop, but it's more efficient to use logarithms.
262 auto const roomToGrow = fl64 - log10Ceil(low);
263 if (roomToGrow > 0)
264 {
265 exponent -= roomToGrow;
266 low *= powerTable[roomToGrow];
267 rem *= powerTable[roomToGrow];
268 }
269 auto const addRem = rem / den128;
270 low += addRem;
271 rem = rem - addRem * den128;
272 }
273
274 // The largest result we can have is ~2^95, which overflows the 64 bit
275 // result we can store in the mantissa. Scale result down by dividing by ten
276 // and adding one to the exponent until the low will fit in the 64-bit
277 // mantissa. Use logarithms to avoid looping.
278 bool hasRem = bool(rem);
279 auto const mustShrink = log10Ceil(low) - fl64;
280 if (mustShrink > 0)
281 {
282 uint128_t const sav(low);
283 exponent += mustShrink;
284 low /= powerTable[mustShrink];
285 if (!hasRem)
286 hasRem = bool(sav - low * powerTable[mustShrink]);
287 }
288
289 std::int64_t mantissa = low.convert_to<std::int64_t>();
290
291 // normalize before rounding
292 if (neg)
293 mantissa *= -1;
294
295 IOUAmount result(mantissa, exponent);
296
297 if (hasRem)
298 {
299 // handle rounding
300 if (roundUp && !neg)
301 {
302 if (!result)
303 {
305 }
306 // This addition cannot overflow because the mantissa is already
307 // normalized
308 return IOUAmount(result.mantissa() + 1, result.exponent());
309 }
310
311 if (!roundUp && neg)
312 {
313 if (!result)
314 {
316 }
317 // This subtraction cannot underflow because `result` is not zero
318 return IOUAmount(result.mantissa() - 1, result.exponent());
319 }
320 }
321
322 return result;
323}
324
325} // namespace ripple
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:46
int exponent() const noexcept
Definition IOUAmount.h:172
std::int64_t mantissa_
Definition IOUAmount.h:48
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition IOUAmount.cpp:75
std::int64_t mantissa() const noexcept
Definition IOUAmount.h:178
IOUAmount & operator+=(IOUAmount const &other)
IOUAmount()=default
static IOUAmount minPositiveAmount()
Definition IOUAmount.cpp:69
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:25
static int constexpr maxExponent
Definition IOUAmount.cpp:66
static int constexpr minExponent
Definition IOUAmount.cpp:65
static std::int64_t constexpr maxMantissa
Definition IOUAmount.cpp:63
static std::int64_t constexpr minMantissa
Definition IOUAmount.cpp:62
void setSTNumberSwitchover(bool v)
Definition IOUAmount.cpp:56
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:630
bool getSTNumberSwitchover()
Definition IOUAmount.cpp:50
T push_back(T... args)
T reserve(T... args)