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/contract.h>
21 #include <ripple/basics/IOUAmount.h>
22 #include <boost/multiprecision/cpp_int.hpp>
23 #include <algorithm>
24 #include <numeric>
25 #include <iterator>
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,
207  [](char c)
208  {
209  return c != '0';
210  });
211 
212  // Crop trailing zeroes. Take advantage of the fact that there's always a
213  // fixed amount of trailing zeroes and skip them.
214  if (std::distance (post_from, post_to) > pad_suffix)
215  post_to -= pad_suffix;
216 
217  assert (post_to >= post_from);
218 
219  post_to = std::find_if(
220  std::make_reverse_iterator (post_to),
221  std::make_reverse_iterator (post_from),
222  [](char c)
223  {
224  return c != '0';
225  }).base();
226 
227  std::string ret;
228 
229  if (negative)
230  ret.append (1, '-');
231 
232  // Assemble the output:
233  if (pre_from == pre_to)
234  ret.append (1, '0');
235  else
236  ret.append(pre_from, pre_to);
237 
238  if (post_to != post_from)
239  {
240  ret.append (1, '.');
241  ret.append (post_from, post_to);
242  }
243 
244  return ret;
245 }
246 
247 IOUAmount
249  IOUAmount const& amt,
250  std::uint32_t num,
251  std::uint32_t den,
252  bool roundUp)
253 {
254  using namespace boost::multiprecision;
255 
256  if (!den)
257  Throw<std::runtime_error> ("division by zero");
258 
259  // A vector with the value 10^index for indexes from 0 to 29
260  // The largest intermediate value we expect is 2^96, which
261  // is less than 10^29
262  static auto const powerTable = []
263  {
264  std::vector<uint128_t> result;
265  result.reserve (30); // 2^96 is largest intermediate result size
266  uint128_t cur (1);
267  for (int i = 0; i < 30; ++i)
268  {
269  result.push_back (cur);
270  cur *= 10;
271  };
272  return result;
273  }();
274 
275  // Return floor(log10(v))
276  // Note: Returns -1 for v == 0
277  static auto log10Floor = [](uint128_t const& v)
278  {
279  // Find the index of the first element >= the requested element, the index
280  // is the log of the element in the log table.
281  auto const l = std::lower_bound (powerTable.begin (), powerTable.end (), v);
282  int index = std::distance (powerTable.begin (), l);
283  // If we're not equal, subtract to get the floor
284  if (*l != v)
285  --index;
286  return index;
287  };
288 
289  // Return ceil(log10(v))
290  static auto log10Ceil = [](uint128_t const& v)
291  {
292  // Find the index of the first element >= the requested element, the index
293  // is the log of the element in the log table.
294  auto const l = std::lower_bound (powerTable.begin (), powerTable.end (), v);
295  return int(std::distance (powerTable.begin (), l));
296  };
297 
298  static auto const fl64 =
300 
301  bool const neg = amt.mantissa () < 0;
302  uint128_t const den128 (den);
303  // a 32 value * a 64 bit value and stored in a 128 bit value. This will never overflow
304  uint128_t const mul =
305  uint128_t (neg ? -amt.mantissa () : amt.mantissa ()) * uint128_t (num);
306 
307  auto low = mul / den128;
308  uint128_t rem (mul - low * den128);
309 
310  int exponent = amt.exponent ();
311 
312  if (rem)
313  {
314  // Mathematically, the result is low + rem/den128. However, since this
315  // uses integer division rem/den128 will be zero. Scale the result so
316  // low does not overflow the largest amount we can store in the mantissa
317  // and (rem/den128) is as large as possible. Scale by multiplying low
318  // and rem by 10 and subtracting one from the exponent. We could do this
319  // with a loop, but it's more efficient to use logarithms.
320  auto const roomToGrow = fl64 - log10Ceil (low);
321  if (roomToGrow > 0)
322  {
323  exponent -= roomToGrow;
324  low *= powerTable[roomToGrow];
325  rem *= powerTable[roomToGrow];
326  }
327  auto const addRem = rem / den128;
328  low += addRem;
329  rem = rem - addRem * den128;
330  }
331 
332  // The largest result we can have is ~2^95, which overflows the 64 bit
333  // result we can store in the mantissa. Scale result down by dividing by ten
334  // and adding one to the exponent until the low will fit in the 64-bit
335  // mantissa. Use logarithms to avoid looping.
336  bool hasRem = bool(rem);
337  auto const mustShrink = log10Ceil (low) - fl64;
338  if (mustShrink > 0)
339  {
340  uint128_t const sav (low);
341  exponent += mustShrink;
342  low /= powerTable[mustShrink];
343  if (!hasRem)
344  hasRem = bool(sav - low * powerTable[mustShrink]);
345  }
346 
347  std::int64_t mantissa = low.convert_to<std::int64_t> ();
348 
349  // normalize before rounding
350  if (neg)
351  mantissa *= -1;
352 
353  IOUAmount result (mantissa, exponent);
354 
355  if (hasRem)
356  {
357  // handle rounding
358  if (roundUp && !neg)
359  {
360  if (!result)
361  {
363  }
364  // This addition cannot overflow because the mantissa is already normalized
365  return IOUAmount (result.mantissa () + 1, result.exponent ());
366  }
367 
368  if (!roundUp && neg)
369  {
370  if (!result)
371  {
372  return IOUAmount (-minMantissa, minExponent);
373  }
374  // This subtraction cannot underflow because `result` is not zero
375  return IOUAmount (result.mantissa () - 1, result.exponent ());
376  }
377  }
378 
379  return result;
380 }
381 
382 
383 }
ripple::mulRatio
IOUAmount mulRatio(IOUAmount const &amt, std::uint32_t num, std::uint32_t den, bool roundUp)
Definition: IOUAmount.cpp:248
ripple::IOUAmount::exponent
int exponent() const noexcept
Definition: IOUAmount.h:126
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:41
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:46
ripple::IOUAmount::exponent_
int exponent_
Definition: IOUAmount.h:47
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:132
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