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 /* The range for the mantissa when normalized */
31 static std::int64_t const minMantissa = 1000000000000000ull;
32 static std::int64_t const maxMantissa = 9999999999999999ull;
33 /* The range for the exponent when normalized */
34 static int const minExponent = -96;
35 static int const maxExponent = 80;
36 
37 void
39 {
40  if (mantissa_ == 0)
41  {
42  *this = beast::zero;
43  return;
44  }
45 
46  bool const negative = (mantissa_ < 0);
47 
48  if (negative)
50 
51  while ((mantissa_ < minMantissa) && (exponent_ > minExponent))
52  {
53  mantissa_ *= 10;
54  --exponent_;
55  }
56 
57  while (mantissa_ > maxMantissa)
58  {
59  if (exponent_ >= maxExponent)
60  Throw<std::overflow_error>("IOUAmount::normalize");
61 
62  mantissa_ /= 10;
63  ++exponent_;
64  }
65 
67  {
68  *this = beast::zero;
69  return;
70  }
71 
72  if (exponent_ > maxExponent)
73  Throw<std::overflow_error>("value overflow");
74 
75  if (negative)
77 }
78 
79 IOUAmount&
81 {
82  if (other == beast::zero)
83  return *this;
84 
85  if (*this == beast::zero)
86  {
87  *this = other;
88  return *this;
89  }
90 
91  auto m = other.mantissa_;
92  auto e = other.exponent_;
93 
94  while (exponent_ < e)
95  {
96  mantissa_ /= 10;
97  ++exponent_;
98  }
99 
100  while (e < exponent_)
101  {
102  m /= 10;
103  ++e;
104  }
105 
106  // This addition cannot overflow an std::int64_t but we may throw from
107  // normalize if the result isn't representable.
108  mantissa_ += m;
109 
110  if (mantissa_ >= -10 && mantissa_ <= 10)
111  {
112  *this = beast::zero;
113  return *this;
114  }
115 
116  normalize();
117 
118  return *this;
119 }
120 
121 bool
122 IOUAmount::operator<(IOUAmount const& other) const
123 {
124  // If the two amounts have different signs (zero is treated as positive)
125  // then the comparison is true iff the left is negative.
126  bool const lneg = mantissa_ < 0;
127  bool const rneg = other.mantissa_ < 0;
128 
129  if (lneg != rneg)
130  return lneg;
131 
132  // Both have same sign and the left is zero: the right must be
133  // greater than 0.
134  if (mantissa_ == 0)
135  return other.mantissa_ > 0;
136 
137  // Both have same sign, the right is zero and the left is non-zero.
138  if (other.mantissa_ == 0)
139  return false;
140 
141  // Both have the same sign, compare by exponents:
142  if (exponent_ > other.exponent_)
143  return lneg;
144  if (exponent_ < other.exponent_)
145  return !lneg;
146 
147  // If equal exponents, compare mantissas
148  return mantissa_ < other.mantissa_;
149 }
150 
152 to_string(IOUAmount const& amount)
153 {
154  // keep full internal accuracy, but make more human friendly if possible
155  if (amount == beast::zero)
156  return "0";
157 
158  int const exponent = amount.exponent();
159  auto mantissa = amount.mantissa();
160 
161  // Use scientific notation for exponents that are too small or too large
162  if (((exponent != 0) && ((exponent < -25) || (exponent > -5))))
163  {
164  std::string ret = std::to_string(mantissa);
165  ret.append(1, 'e');
166  ret.append(std::to_string(exponent));
167  return ret;
168  }
169 
170  bool negative = false;
171 
172  if (mantissa < 0)
173  {
174  mantissa = -mantissa;
175  negative = true;
176  }
177 
178  assert(exponent + 43 > 0);
179 
180  size_t const pad_prefix = 27;
181  size_t const pad_suffix = 23;
182 
183  std::string const raw_value(std::to_string(mantissa));
184  std::string val;
185 
186  val.reserve(raw_value.length() + pad_prefix + pad_suffix);
187  val.append(pad_prefix, '0');
188  val.append(raw_value);
189  val.append(pad_suffix, '0');
190 
191  size_t const offset(exponent + 43);
192 
193  auto pre_from(val.begin());
194  auto const pre_to(val.begin() + offset);
195 
196  auto const post_from(val.begin() + offset);
197  auto post_to(val.end());
198 
199  // Crop leading zeroes. Take advantage of the fact that there's always a
200  // fixed amount of leading zeroes and skip them.
201  if (std::distance(pre_from, pre_to) > pad_prefix)
202  pre_from += pad_prefix;
203 
204  assert(post_to >= post_from);
205 
206  pre_from = std::find_if(pre_from, pre_to, [](char c) { return c != '0'; });
207 
208  // Crop trailing zeroes. Take advantage of the fact that there's always a
209  // fixed amount of trailing zeroes and skip them.
210  if (std::distance(post_from, post_to) > pad_suffix)
211  post_to -= pad_suffix;
212 
213  assert(post_to >= post_from);
214 
215  post_to = std::find_if(
217  std::make_reverse_iterator(post_from),
218  [](char c) { return c != '0'; })
219  .base();
220 
221  std::string ret;
222 
223  if (negative)
224  ret.append(1, '-');
225 
226  // Assemble the output:
227  if (pre_from == pre_to)
228  ret.append(1, '0');
229  else
230  ret.append(pre_from, pre_to);
231 
232  if (post_to != post_from)
233  {
234  ret.append(1, '.');
235  ret.append(post_from, post_to);
236  }
237 
238  return ret;
239 }
240 
241 IOUAmount
243  IOUAmount const& amt,
244  std::uint32_t num,
245  std::uint32_t den,
246  bool roundUp)
247 {
248  using namespace boost::multiprecision;
249 
250  if (!den)
251  Throw<std::runtime_error>("division by zero");
252 
253  // A vector with the value 10^index for indexes from 0 to 29
254  // The largest intermediate value we expect is 2^96, which
255  // is less than 10^29
256  static auto const powerTable = [] {
257  std::vector<uint128_t> result;
258  result.reserve(30); // 2^96 is largest intermediate result size
259  uint128_t cur(1);
260  for (int i = 0; i < 30; ++i)
261  {
262  result.push_back(cur);
263  cur *= 10;
264  };
265  return result;
266  }();
267 
268  // Return floor(log10(v))
269  // Note: Returns -1 for v == 0
270  static auto log10Floor = [](uint128_t const& v) {
271  // Find the index of the first element >= the requested element, the
272  // index is the log of the element in the log table.
273  auto const l =
274  std::lower_bound(powerTable.begin(), powerTable.end(), v);
275  int index = std::distance(powerTable.begin(), l);
276  // If we're not equal, subtract to get the floor
277  if (*l != v)
278  --index;
279  return index;
280  };
281 
282  // Return ceil(log10(v))
283  static auto log10Ceil = [](uint128_t const& v) {
284  // Find the index of the first element >= the requested element, the
285  // index is the log of the element in the log table.
286  auto const l =
287  std::lower_bound(powerTable.begin(), powerTable.end(), v);
288  return int(std::distance(powerTable.begin(), l));
289  };
290 
291  static auto const fl64 =
293 
294  bool const neg = amt.mantissa() < 0;
295  uint128_t const den128(den);
296  // a 32 value * a 64 bit value and stored in a 128 bit value. This will
297  // never overflow
298  uint128_t const mul =
299  uint128_t(neg ? -amt.mantissa() : amt.mantissa()) * uint128_t(num);
300 
301  auto low = mul / den128;
302  uint128_t rem(mul - low * den128);
303 
304  int exponent = amt.exponent();
305 
306  if (rem)
307  {
308  // Mathematically, the result is low + rem/den128. However, since this
309  // uses integer division rem/den128 will be zero. Scale the result so
310  // low does not overflow the largest amount we can store in the mantissa
311  // and (rem/den128) is as large as possible. Scale by multiplying low
312  // and rem by 10 and subtracting one from the exponent. We could do this
313  // with a loop, but it's more efficient to use logarithms.
314  auto const roomToGrow = fl64 - log10Ceil(low);
315  if (roomToGrow > 0)
316  {
317  exponent -= roomToGrow;
318  low *= powerTable[roomToGrow];
319  rem *= powerTable[roomToGrow];
320  }
321  auto const addRem = rem / den128;
322  low += addRem;
323  rem = rem - addRem * den128;
324  }
325 
326  // The largest result we can have is ~2^95, which overflows the 64 bit
327  // result we can store in the mantissa. Scale result down by dividing by ten
328  // and adding one to the exponent until the low will fit in the 64-bit
329  // mantissa. Use logarithms to avoid looping.
330  bool hasRem = bool(rem);
331  auto const mustShrink = log10Ceil(low) - fl64;
332  if (mustShrink > 0)
333  {
334  uint128_t const sav(low);
335  exponent += mustShrink;
336  low /= powerTable[mustShrink];
337  if (!hasRem)
338  hasRem = bool(sav - low * powerTable[mustShrink]);
339  }
340 
341  std::int64_t mantissa = low.convert_to<std::int64_t>();
342 
343  // normalize before rounding
344  if (neg)
345  mantissa *= -1;
346 
347  IOUAmount result(mantissa, exponent);
348 
349  if (hasRem)
350  {
351  // handle rounding
352  if (roundUp && !neg)
353  {
354  if (!result)
355  {
357  }
358  // This addition cannot overflow because the mantissa is already
359  // normalized
360  return IOUAmount(result.mantissa() + 1, result.exponent());
361  }
362 
363  if (!roundUp && neg)
364  {
365  if (!result)
366  {
368  }
369  // This subtraction cannot underflow because `result` is not zero
370  return IOUAmount(result.mantissa() - 1, result.exponent());
371  }
372  }
373 
374  return result;
375 }
376 
377 } // namespace ripple
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:242
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:122
std::string
STL class.
std::string::reserve
T reserve(T... args)
ripple::IOUAmount::normalize
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition: IOUAmount.cpp:38
std::vector
STL class.
std::find_if
T find_if(T... args)
std::string::length
T length(T... args)
iterator
std::distance
T distance(T... args)
ripple::maxMantissa
static const std::int64_t maxMantissa
Definition: IOUAmount.cpp:32
ripple::IOUAmount
Floating point representation of amounts with high dynamic range.
Definition: IOUAmount.h:41
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:45
algorithm
std::vector::push_back
T push_back(T... args)
ripple::IOUAmount::operator<
bool operator<(IOUAmount const &other) const
Definition: IOUAmount.cpp:122
stdexcept
ripple::IOUAmount::mantissa_
std::int64_t mantissa_
Definition: IOUAmount.h:45
ripple::IOUAmount::exponent_
int exponent_
Definition: IOUAmount.h:46
std::to_string
T to_string(T... args)
ripple::maxExponent
static const int maxExponent
Definition: IOUAmount.cpp:35
std::int64_t
ripple::IOUAmount::operator+=
IOUAmount & operator+=(IOUAmount const &other)
Definition: IOUAmount.cpp:80
std::string::append
T append(T... args)
ripple::minMantissa
static const std::int64_t minMantissa
Definition: IOUAmount.cpp:31
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:128
std::lower_bound
T lower_bound(T... args)
std::string::begin
T begin(T... args)
std::string::end
T end(T... args)
ripple::minExponent
static const int minExponent
Definition: IOUAmount.cpp:34
numeric
std::make_reverse_iterator
T make_reverse_iterator(T... args)
std::numeric_limits