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 
39 {
41 }
42 
43 void
45 {
46  if (mantissa_ == 0)
47  {
48  *this = beast::zero;
49  return;
50  }
51 
52  bool const negative = (mantissa_ < 0);
53 
54  if (negative)
56 
57  while ((mantissa_ < minMantissa) && (exponent_ > minExponent))
58  {
59  mantissa_ *= 10;
60  --exponent_;
61  }
62 
63  while (mantissa_ > maxMantissa)
64  {
65  if (exponent_ >= maxExponent)
66  Throw<std::overflow_error>("IOUAmount::normalize");
67 
68  mantissa_ /= 10;
69  ++exponent_;
70  }
71 
73  {
74  *this = beast::zero;
75  return;
76  }
77 
78  if (exponent_ > maxExponent)
79  Throw<std::overflow_error>("value overflow");
80 
81  if (negative)
83 }
84 
85 IOUAmount&
87 {
88  if (other == beast::zero)
89  return *this;
90 
91  if (*this == beast::zero)
92  {
93  *this = other;
94  return *this;
95  }
96 
97  auto m = other.mantissa_;
98  auto e = other.exponent_;
99 
100  while (exponent_ < e)
101  {
102  mantissa_ /= 10;
103  ++exponent_;
104  }
105 
106  while (e < exponent_)
107  {
108  m /= 10;
109  ++e;
110  }
111 
112  // This addition cannot overflow an std::int64_t but we may throw from
113  // normalize if the result isn't representable.
114  mantissa_ += m;
115 
116  if (mantissa_ >= -10 && mantissa_ <= 10)
117  {
118  *this = beast::zero;
119  return *this;
120  }
121 
122  normalize();
123 
124  return *this;
125 }
126 
127 bool
128 IOUAmount::operator<(IOUAmount const& other) const
129 {
130  // If the two amounts have different signs (zero is treated as positive)
131  // then the comparison is true iff the left is negative.
132  bool const lneg = mantissa_ < 0;
133  bool const rneg = other.mantissa_ < 0;
134 
135  if (lneg != rneg)
136  return lneg;
137 
138  // Both have same sign and the left is zero: the right must be
139  // greater than 0.
140  if (mantissa_ == 0)
141  return other.mantissa_ > 0;
142 
143  // Both have same sign, the right is zero and the left is non-zero.
144  if (other.mantissa_ == 0)
145  return false;
146 
147  // Both have the same sign, compare by exponents:
148  if (exponent_ > other.exponent_)
149  return lneg;
150  if (exponent_ < other.exponent_)
151  return !lneg;
152 
153  // If equal exponents, compare mantissas
154  return mantissa_ < other.mantissa_;
155 }
156 
158 to_string(IOUAmount const& amount)
159 {
160  // keep full internal accuracy, but make more human friendly if possible
161  if (amount == beast::zero)
162  return "0";
163 
164  int const exponent = amount.exponent();
165  auto mantissa = amount.mantissa();
166 
167  // Use scientific notation for exponents that are too small or too large
168  if (((exponent != 0) && ((exponent < -25) || (exponent > -5))))
169  {
170  std::string ret = std::to_string(mantissa);
171  ret.append(1, 'e');
172  ret.append(std::to_string(exponent));
173  return ret;
174  }
175 
176  bool negative = false;
177 
178  if (mantissa < 0)
179  {
180  mantissa = -mantissa;
181  negative = true;
182  }
183 
184  assert(exponent + 43 > 0);
185 
186  size_t const pad_prefix = 27;
187  size_t const pad_suffix = 23;
188 
189  std::string const raw_value(std::to_string(mantissa));
190  std::string val;
191 
192  val.reserve(raw_value.length() + pad_prefix + pad_suffix);
193  val.append(pad_prefix, '0');
194  val.append(raw_value);
195  val.append(pad_suffix, '0');
196 
197  size_t const offset(exponent + 43);
198 
199  auto pre_from(val.begin());
200  auto const pre_to(val.begin() + offset);
201 
202  auto const post_from(val.begin() + offset);
203  auto post_to(val.end());
204 
205  // Crop leading zeroes. Take advantage of the fact that there's always a
206  // fixed amount of leading zeroes and skip them.
207  if (std::distance(pre_from, pre_to) > pad_prefix)
208  pre_from += pad_prefix;
209 
210  assert(post_to >= post_from);
211 
212  pre_from = std::find_if(pre_from, pre_to, [](char c) { return c != '0'; });
213 
214  // Crop trailing zeroes. Take advantage of the fact that there's always a
215  // fixed amount of trailing zeroes and skip them.
216  if (std::distance(post_from, post_to) > pad_suffix)
217  post_to -= pad_suffix;
218 
219  assert(post_to >= post_from);
220 
221  post_to = std::find_if(
223  std::make_reverse_iterator(post_from),
224  [](char c) { 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  std::vector<uint128_t> result;
264  result.reserve(30); // 2^96 is largest intermediate result size
265  uint128_t cur(1);
266  for (int i = 0; i < 30; ++i)
267  {
268  result.push_back(cur);
269  cur *= 10;
270  };
271  return result;
272  }();
273 
274  // Return floor(log10(v))
275  // Note: Returns -1 for v == 0
276  static auto log10Floor = [](uint128_t const& v) {
277  // Find the index of the first element >= the requested element, the
278  // index is the log of the element in the log table.
279  auto const l =
280  std::lower_bound(powerTable.begin(), powerTable.end(), v);
281  int index = std::distance(powerTable.begin(), l);
282  // If we're not equal, subtract to get the floor
283  if (*l != v)
284  --index;
285  return index;
286  };
287 
288  // Return ceil(log10(v))
289  static auto log10Ceil = [](uint128_t const& v) {
290  // Find the index of the first element >= the requested element, the
291  // index is the log of the element in the log table.
292  auto const l =
293  std::lower_bound(powerTable.begin(), powerTable.end(), v);
294  return int(std::distance(powerTable.begin(), l));
295  };
296 
297  static auto const fl64 =
299 
300  bool const neg = amt.mantissa() < 0;
301  uint128_t const den128(den);
302  // a 32 value * a 64 bit value and stored in a 128 bit value. This will
303  // 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
365  // normalized
366  return IOUAmount(result.mantissa() + 1, result.exponent());
367  }
368 
369  if (!roundUp && neg)
370  {
371  if (!result)
372  {
374  }
375  // This subtraction cannot underflow because `result` is not zero
376  return IOUAmount(result.mantissa() - 1, result.exponent());
377  }
378  }
379 
380  return result;
381 }
382 
383 } // namespace ripple
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:122
std::string
STL class.
ripple::IOUAmount::minPositiveAmount
static IOUAmount minPositiveAmount()
Definition: IOUAmount.cpp:38
std::string::reserve
T reserve(T... args)
ripple::IOUAmount::normalize
void normalize()
Adjusts the mantissa and exponent to the proper range.
Definition: IOUAmount.cpp:44
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
algorithm
std::vector::push_back
T push_back(T... args)
ripple::IOUAmount::operator<
bool operator<(IOUAmount const &other) const
Definition: IOUAmount.cpp:128
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
ripple::IOUAmount::IOUAmount
IOUAmount()=default
std::int64_t
ripple::IOUAmount::operator+=
IOUAmount & operator+=(IOUAmount const &other)
Definition: IOUAmount.cpp:86
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)
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
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