rippled
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 <ripple/basics/IOUAmount.h>
21 #include <ripple/basics/contract.h>
22 #include <boost/multiprecision/cpp_int.hpp>
23 #include <algorithm>
24 #include <iterator>
25 #include <numeric>
26 #include <stdexcept>
27 
28 namespace ripple {
29 
30 namespace {
31 
32 // Use a static inside a function to help prevent order-of-initialzation issues
33 LocalValue<bool>&
34 getStaticSTNumberSwitchover()
35 {
36  static LocalValue<bool> r{true};
37  return r;
38 }
39 } // namespace
40 
41 bool
43 {
44  return *getStaticSTNumberSwitchover();
45 }
46 
47 void
49 {
50  *getStaticSTNumberSwitchover() = v;
51 }
52 
53 /* The range for the mantissa when normalized */
54 static std::int64_t constexpr minMantissa = 1000000000000000ull;
55 static std::int64_t constexpr maxMantissa = 9999999999999999ull;
56 /* The range for the exponent when normalized */
57 static int constexpr minExponent = -96;
58 static int constexpr maxExponent = 80;
59 
62 {
64 }
65 
66 void
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();
80  if (exponent_ > maxExponent)
81  Throw<std::overflow_error>("value overflow");
82  if (exponent_ < minExponent)
83  *this = beast::zero;
84  return;
85  }
86 
87  bool const negative = (mantissa_ < 0);
88 
89  if (negative)
91 
92  while ((mantissa_ < minMantissa) && (exponent_ > minExponent))
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 
113  if (exponent_ > maxExponent)
114  Throw<std::overflow_error>("value overflow");
115 
116  if (negative)
117  mantissa_ = -mantissa_;
118 }
119 
121  : mantissa_(other.mantissa()), exponent_(other.exponent())
122 {
123  if (exponent_ > maxExponent)
124  Throw<std::overflow_error>("value overflow");
125  if (exponent_ < minExponent)
126  *this = beast::zero;
127 }
128 
129 IOUAmount&
131 {
132  if (other == beast::zero)
133  return *this;
134 
135  if (*this == beast::zero)
136  {
137  *this = other;
138  return *this;
139  }
140 
141  if (getSTNumberSwitchover())
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 
176 to_string(IOUAmount const& amount)
177 {
178  return to_string(Number{amount});
179 }
180 
181 IOUAmount
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 = [] {
197  std::vector<uint128_t> result;
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
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:182
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:163
ripple::maxExponent
static constexpr int maxExponent
Definition: IOUAmount.cpp:58
ripple::setSTNumberSwitchover
void setSTNumberSwitchover(bool v)
Definition: IOUAmount.cpp:48
std::string
STL class.
ripple::IOUAmount::minPositiveAmount
static IOUAmount minPositiveAmount()
Definition: IOUAmount.cpp:61
std::vector::reserve
T reserve(T... args)
ripple::IOUAmount::normalize
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition: IOUAmount.cpp:67
std::vector
STL class.
iterator
std::distance
T distance(T... args)
ripple::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:43
algorithm
std::vector::push_back
T push_back(T... args)
stdexcept
ripple::Number
Definition: Number.h:36
ripple::IOUAmount::mantissa_
std::int64_t mantissa_
Definition: IOUAmount.h:47
ripple::IOUAmount::exponent_
int exponent_
Definition: IOUAmount.h:48
ripple::maxMantissa
static constexpr std::int64_t maxMantissa
Definition: IOUAmount.cpp:55
ripple::minExponent
static constexpr int minExponent
Definition: IOUAmount.cpp:57
ripple::IOUAmount::IOUAmount
IOUAmount()=default
std::int64_t
ripple::IOUAmount::operator+=
IOUAmount & operator+=(IOUAmount const &other)
Definition: IOUAmount.cpp:130
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::IOUAmount::mantissa
std::int64_t mantissa() const noexcept
Definition: IOUAmount.h:169
std::lower_bound
T lower_bound(T... args)
ripple::getSTNumberSwitchover
bool getSTNumberSwitchover()
Definition: IOUAmount.cpp:42
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
numeric
std::numeric_limits
ripple::minMantissa
static constexpr std::int64_t minMantissa
Definition: IOUAmount.cpp:54