xrpld
Toggle main menu visibility
Loading...
Searching...
No Matches
libxrpl
protocol
libxrpl/protocol/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
17
namespace
xrpl
{
18
19
/* The range for the mantissa when normalized */
20
// log(2^63,10) ~ 18.96
21
//
22
static
constexpr
std::int64_t
kMinMantissa
=
STAmount::kMinValue
;
23
static
constexpr
std::int64_t
kMaxMantissa
=
STAmount::kMaxValue
;
24
/* The range for the exponent when normalized */
25
static
constexpr
int
kMinExponent
=
STAmount::kMinOffset
;
26
static
constexpr
int
kMaxExponent
=
STAmount::kMaxOffset
;
27
28
IOUAmount
29
IOUAmount::fromNumber
(
Number
const
& number)
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_
) =
35
number.
normalizeToRange
<
kMinMantissa
,
kMaxMantissa
>();
36
return
result;
37
}
38
39
IOUAmount
40
IOUAmount::minPositiveAmount
()
41
{
42
return
IOUAmount
(
kMinMantissa
,
kMinExponent
);
43
}
44
45
void
46
IOUAmount::normalize
()
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
58
IOUAmount::IOUAmount
(
Number
const
& other) :
IOUAmount
(
fromNumber
(other))
59
{
60
if
(
exponent_
>
kMaxExponent
)
61
{
62
Throw<std::overflow_error>
(
"value overflow"
);
63
}
64
if
(
exponent_
<
kMinExponent
)
65
{
66
*
this
=
beast::kZero
;
67
}
68
}
69
70
IOUAmount
&
71
IOUAmount::operator+=
(
IOUAmount
const
& other)
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
86
std::string
87
to_string
(
IOUAmount
const
& amount)
88
{
89
return
to_string
(
Number
{amount});
90
}
91
92
IOUAmount
93
mulRatio
(
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 = [] {
104
std::vector<uint128_t>
result;
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
auto
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
{
199
return
IOUAmount::minPositiveAmount
();
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
{
210
return
IOUAmount
(-
kMinMantissa
,
kMinExponent
);
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
algorithm
std::string
xrpl::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition
IOUAmount.h:26
xrpl::IOUAmount::operator+=
IOUAmount & operator+=(IOUAmount const &other)
Definition
libxrpl/protocol/IOUAmount.cpp:71
xrpl::IOUAmount::mantissa_
MantissaType mantissa_
Definition
IOUAmount.h:30
xrpl::IOUAmount::IOUAmount
IOUAmount()=default
xrpl::IOUAmount::fromNumber
static IOUAmount fromNumber(Number const &number)
Definition
libxrpl/protocol/IOUAmount.cpp:29
xrpl::IOUAmount::exponent_
ExponentType exponent_
Definition
IOUAmount.h:31
xrpl::IOUAmount::mantissa
MantissaType mantissa() const noexcept
Definition
IOUAmount.h:172
xrpl::IOUAmount::exponent
ExponentType exponent() const noexcept
Definition
IOUAmount.h:166
xrpl::IOUAmount::minPositiveAmount
static IOUAmount minPositiveAmount()
Definition
libxrpl/protocol/IOUAmount.cpp:40
xrpl::IOUAmount::normalize
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition
libxrpl/protocol/IOUAmount.cpp:46
xrpl::Number
Number is a floating point type that can represent a wide range of values.
Definition
Number.h:351
xrpl::Number::normalizeToRange
std::pair< T, int > normalizeToRange() const
Definition
Number.h:842
xrpl::STAmount::kMinOffset
static constexpr int kMinOffset
Definition
STAmount.h:61
xrpl::STAmount::kMinValue
static constexpr std::uint64_t kMinValue
Definition
STAmount.h:65
xrpl::STAmount::kMaxValue
static constexpr std::uint64_t kMaxValue
Definition
STAmount.h:67
xrpl::STAmount::kMaxOffset
static constexpr int kMaxOffset
Definition
STAmount.h:62
cstdint
std::distance
T distance(T... args)
std::int64_t
iterator
limits
std::ranges::lower_bound
T lower_bound(T... args)
std::numeric_limits::max
T max(T... args)
beast::kZero
constexpr Zero kZero
Definition
Zero.h:30
xrpl
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition
algorithm.h:5
xrpl::kMinExponent
static constexpr int kMinExponent
Definition
libxrpl/protocol/IOUAmount.cpp:25
xrpl::kMinMantissa
static constexpr std::int64_t kMinMantissa
Definition
libxrpl/protocol/IOUAmount.cpp:22
xrpl::to_string
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition
base_uint.h:657
xrpl::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition
libxrpl/protocol/IOUAmount.cpp:93
xrpl::kMaxMantissa
static constexpr std::int64_t kMaxMantissa
Definition
libxrpl/protocol/IOUAmount.cpp:23
xrpl::kMaxExponent
static constexpr int kMaxExponent
Definition
libxrpl/protocol/IOUAmount.cpp:26
xrpl::Throw
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition
contract.h:52
std::vector::push_back
T push_back(T... args)
std::vector::reserve
T reserve(T... args)
stdexcept
string
std::tie
T tie(T... args)
tuple
vector
Generated by
1.17.0