Add conversions between Number, XRPAmount and int64_t

* Conversions to Number are implicit
* Conversions away from Number are explicit and potentially lossy
* If lossy, round to nearest, and to even on tie
This commit is contained in:
Howard Hinnant
2022-04-15 16:20:52 -04:00
committed by Elliot Lee
parent 0ee63b7c7b
commit 476ee8a479
3 changed files with 117 additions and 0 deletions

View File

@@ -21,6 +21,7 @@
#define RIPPLE_BASICS_NUMBER_H_INCLUDED
#include <ripple/basics/IOUAmount.h>
#include <ripple/basics/XRPAmount.h>
#include <cstdint>
#include <ostream>
#include <string>
@@ -51,6 +52,7 @@ public:
explicit constexpr Number(rep mantissa, int exponent, unchecked) noexcept;
Number(IOUAmount const& x);
Number(XRPAmount const& x);
constexpr rep
mantissa() const noexcept;
@@ -81,6 +83,8 @@ public:
operator/=(Number const& x);
explicit operator IOUAmount() const;
explicit operator XRPAmount() const; // round to nearest, even on tie
explicit operator rep() const; // round to nearest, even on tie
friend constexpr bool
operator==(Number const& x, Number const& y) noexcept
@@ -184,6 +188,10 @@ inline Number::Number(IOUAmount const& x) : Number{x.mantissa(), x.exponent()}
{
}
inline Number::Number(XRPAmount const& x) : Number{x.drops()}
{
}
inline constexpr Number::rep
Number::mantissa() const noexcept
{

View File

@@ -374,6 +374,45 @@ Number::operator/=(Number const& y)
return *this;
}
Number::operator rep() const
{
std::int64_t drops = mantissa_;
int offset = exponent_;
guard g;
if (drops != 0)
{
if (drops < 0)
{
g.set_negative();
drops = -drops;
}
for (; offset < 0; ++offset)
{
g.push(drops % 10);
drops /= 10;
}
for (; offset > 0; --offset)
{
if (drops > std::numeric_limits<decltype(drops)>::max() / 10)
throw std::runtime_error("Number::operator rep() overflow");
drops *= 10;
}
auto r = g.round();
if (r == 1 || (r == 0 && (drops & 1) == 1))
{
++drops;
}
if (g.is_negative())
drops = -drops;
}
return drops;
}
Number::operator XRPAmount() const
{
return XRPAmount{static_cast<rep>(*this)};
}
std::string
to_string(Number const& amount)
{

View File

@@ -126,6 +126,75 @@ public:
BEAST_EXPECT(x == z);
}
void
test_to_integer()
{
Number x[]{
Number{0},
Number{1},
Number{2},
Number{3},
Number{-1},
Number{-2},
Number{-3},
Number{10},
Number{99},
Number{1155},
Number{9'999'999'999'999'999, 0},
Number{9'999'999'999'999'999, 1},
Number{9'999'999'999'999'999, 2},
Number{-9'999'999'999'999'999, 2},
Number{15, -1},
Number{14, -1},
Number{16, -1},
Number{25, -1},
Number{6, -1},
Number{5, -1},
Number{4, -1},
Number{-15, -1},
Number{-14, -1},
Number{-16, -1},
Number{-25, -1},
Number{-6, -1},
Number{-5, -1},
Number{-4, -1}};
std::int64_t y[]{
0,
1,
2,
3,
-1,
-2,
-3,
10,
99,
1155,
9'999'999'999'999'999,
99'999'999'999'999'990,
999'999'999'999'999'900,
-999'999'999'999'999'900,
2,
1,
2,
2,
1,
0,
0,
-2,
-1,
-2,
-2,
-1,
0,
0};
static_assert(std::size(x) == std::size(y));
for (unsigned u = 0; u < std::size(x); ++u)
{
auto j = static_cast<std::int64_t>(x[u]);
BEAST_EXPECT(j == y[u]);
}
}
void
run() override
{
@@ -135,6 +204,7 @@ public:
test_div();
test_root();
testConversions();
test_to_integer();
}
};