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