xrpld
Loading...
Searching...
No Matches
IOUAmount.cpp
1#include <xrpl/protocol/IOUAmount.h>
2
3#include <xrpl/basics/Number.h>
4#include <xrpl/basics/contract.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/protocol/STAmount.h>
7
8#include <algorithm>
9#include <cstdint>
10#include <iterator>
11#include <limits>
12#include <stdexcept>
13#include <string>
14#include <tuple>
15#include <vector>
16
17namespace xrpl {
18
19/* The range for the mantissa when normalized */
20// log(2^63,10) ~ 18.96
21//
24/* The range for the exponent when normalized */
25static constexpr int kMinExponent = STAmount::kMinOffset;
26static constexpr int kMaxExponent = STAmount::kMaxOffset;
27
30{
31 // Need to create a default IOUAmount and assign directly so it doesn't try
32 // to normalize, which calls fromNumber
33 IOUAmount result{};
34 std::tie(result.mantissa_, result.exponent_) =
36 return result;
37}
38
44
45void
47{
48 if (mantissa_ == 0)
49 {
50 *this = beast::kZero;
51 return;
52 }
53
54 Number const v{mantissa_, exponent_};
55 *this = IOUAmount(v);
56}
57
59{
61 {
62 Throw<std::overflow_error>("value overflow");
63 }
65 {
66 *this = beast::kZero;
67 }
68}
69
72{
73 if (other == beast::kZero)
74 return *this;
75
76 if (*this == beast::kZero)
77 {
78 *this = other;
79 return *this;
80 }
81
82 *this = IOUAmount{Number{*this} + Number{other}};
83 return *this;
84}
85
87to_string(IOUAmount const& amount)
88{
89 return to_string(Number{amount});
90}
91
92IOUAmount
93mulRatio(IOUAmount const& amt, std::uint32_t num, std::uint32_t den, bool roundUp)
94{
95 using namespace boost::multiprecision;
96
97 if (den == 0u)
98 Throw<std::runtime_error>("division by zero");
99
100 // A vector with the value 10^index for indexes from 0 to 29
101 // The largest intermediate value we expect is 2^96, which
102 // is less than 10^29
103 static auto const kPowerTable = [] {
105 result.reserve(30); // 2^96 is largest intermediate result size
106 uint128_t cur(1);
107 for (int i = 0; i < 30; ++i)
108 {
109 result.push_back(cur);
110 cur *= 10;
111 };
112 return result;
113 }();
114
115 // Return floor(log10(v))
116 // Note: Returns -1 for v == 0
117 static auto kLoG10Floor = [](uint128_t const& v) {
118 // Find the index of the first element >= the requested element, the
119 // index is the log of the element in the log table.
120 auto const l = std::ranges::lower_bound(kPowerTable, v);
121 int index = std::distance(kPowerTable.begin(), l);
122 // If we're not equal, subtract to get the floor
123 if (*l != v)
124 --index;
125 return index;
126 };
127
128 // Return ceil(log10(v))
129 static auto kLoG10Ceil = [](uint128_t const& v) {
130 // Find the index of the first element >= the requested element, the
131 // index is the log of the element in the log table.
132 auto const l = std::ranges::lower_bound(kPowerTable, v);
133 return int(std::distance(kPowerTable.begin(), l));
134 };
135
136 static auto const kFl64 = kLoG10Floor(std::numeric_limits<std::int64_t>::max());
137
138 bool const neg = amt.mantissa() < 0;
139 uint128_t const den128(den);
140 // a 32 value * a 64 bit value and stored in a 128 bit value. This will
141 // never overflow
142 uint128_t const mul = uint128_t(neg ? -amt.mantissa() : amt.mantissa()) * uint128_t(num);
143
144 auto low = mul / den128;
145 uint128_t rem(mul - low * den128);
146
147 int exponent = amt.exponent();
148
149 if (rem)
150 {
151 // Mathematically, the result is low + rem/den128. However, since this
152 // uses integer division rem/den128 will be zero. Scale the result so
153 // low does not overflow the largest amount we can store in the mantissa
154 // and (rem/den128) is as large as possible. Scale by multiplying low
155 // and rem by 10 and subtracting one from the exponent. We could do this
156 // with a loop, but it's more efficient to use logarithms.
157 auto const roomToGrow = kFl64 - kLoG10Ceil(low);
158 if (roomToGrow > 0)
159 {
160 exponent -= roomToGrow;
161 low *= kPowerTable[roomToGrow];
162 rem *= kPowerTable[roomToGrow];
163 }
164 auto const addRem = rem / den128;
165 low += addRem;
166 rem = rem - addRem * den128;
167 }
168
169 // The largest result we can have is ~2^95, which overflows the 64 bit
170 // result we can store in the mantissa. Scale result down by dividing by ten
171 // and adding one to the exponent until the low will fit in the 64-bit
172 // mantissa. Use logarithms to avoid looping.
173 bool hasRem = bool(rem);
174 auto const mustShrink = kLoG10Ceil(low) - kFl64;
175 if (mustShrink > 0)
176 {
177 uint128_t const sav(low);
178 exponent += mustShrink;
179 low /= kPowerTable[mustShrink];
180 if (!hasRem)
181 hasRem = bool(sav - low * kPowerTable[mustShrink]);
182 }
183
184 std::int64_t mantissa = low.convert_to<std::int64_t>();
185
186 // normalize before rounding
187 if (neg)
188 mantissa *= -1;
189
190 IOUAmount result(mantissa, exponent);
191
192 if (hasRem)
193 {
194 // handle rounding
195 if (roundUp && !neg)
196 {
197 if (!result)
198 {
200 }
201 // This addition cannot overflow because the mantissa is already
202 // normalized
203 return IOUAmount(result.mantissa() + 1, result.exponent());
204 }
205
206 if (!roundUp && neg)
207 {
208 if (!result)
209 {
211 }
212 // This subtraction cannot underflow because `result` is not zero
213 return IOUAmount(result.mantissa() - 1, result.exponent());
214 }
215 }
216
217 return result;
218}
219
220} // namespace xrpl
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:24
IOUAmount & operator+=(IOUAmount const &other)
Definition IOUAmount.cpp:71
mantissa_type mantissa() const noexcept
Definition IOUAmount.h:165
exponent_type exponent_
Definition IOUAmount.h:29
IOUAmount()=default
exponent_type exponent() const noexcept
Definition IOUAmount.h:159
static IOUAmount fromNumber(Number const &number)
Definition IOUAmount.cpp:29
mantissa_type mantissa_
Definition IOUAmount.h:28
static IOUAmount minPositiveAmount()
Definition IOUAmount.cpp:40
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition IOUAmount.cpp:46
Number is a floating point type that can represent a wide range of values.
Definition Number.h:306
std::pair< T, int > normalizeToRange() const
Definition Number.h:789
static constexpr int kMinOffset
Definition STAmount.h:47
static constexpr std::uint64_t kMinValue
Definition STAmount.h:51
static constexpr std::uint64_t kMaxValue
Definition STAmount.h:53
static constexpr int kMaxOffset
Definition STAmount.h:48
T distance(T... args)
T lower_bound(T... args)
T max(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static constexpr int kMinExponent
Definition IOUAmount.cpp:25
static constexpr std::int64_t kMinMantissa
Definition IOUAmount.cpp:22
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition IOUAmount.cpp:93
static constexpr std::int64_t kMaxMantissa
Definition IOUAmount.cpp:23
static constexpr int kMaxExponent
Definition IOUAmount.cpp:26
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
T push_back(T... args)
T reserve(T... args)
T tie(T... args)