Float point Hostfunctions unit tests (#5656)

* Added direct unittests for float hostfunctions
This commit is contained in:
Olek
2025-08-14 10:21:21 -04:00
committed by GitHub
parent 5dc0cee28a
commit 0d0e279ae2
4 changed files with 992 additions and 127 deletions

View File

@@ -58,6 +58,13 @@ private:
normalize();
public:
/* The range for the mantissa when normalized */
static std::int64_t constexpr minMantissa = 1000000000000000ull;
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
/* The range for the exponent when normalized */
static int constexpr minExponent = -96;
static int constexpr maxExponent = 80;
IOUAmount() = default;
explicit IOUAmount(Number const& other);
IOUAmount(beast::Zero);

View File

@@ -58,13 +58,6 @@ setSTNumberSwitchover(bool v)
*getStaticSTNumberSwitchover() = v;
}
/* The range for the mantissa when normalized */
static std::int64_t constexpr minMantissa = 1000000000000000ull;
static std::int64_t constexpr maxMantissa = 9999999999999999ull;
/* The range for the exponent when normalized */
static int constexpr minExponent = -96;
static int constexpr maxExponent = 80;
IOUAmount
IOUAmount::minPositiveAmount()
{
@@ -312,7 +305,8 @@ mulRatio(
{
if (!result)
{
return IOUAmount(-minMantissa, minExponent);
return IOUAmount(
-IOUAmount::minMantissa, IOUAmount::minExponent);
}
// This subtraction cannot underflow because `result` is not zero
return IOUAmount(result.mantissa() - 1, result.exponent());

File diff suppressed because it is too large Load Diff

View File

@@ -825,9 +825,11 @@ WasmHostFunctionsImpl::floatLog(Slice const& x, int32_t mode)
class Number2 : public Number
{
bool good_;
public:
enum Issue { XRP, MPT, IOU };
protected:
bool good_;
Issue issue_;
public:
@@ -847,8 +849,9 @@ public:
int32_t const e =
static_cast<uint8_t>((v >> (64 - 10)) & ((1ull << 8) - 1));
int64_t const m = neg * (v & ((1ull << 54) - 1));
x = !m ? Number() : Number(m, e - 97);
if (m && (x.exponent() > 80 || x.exponent() < -96))
x = !m || (e < 1) ? Number()
: Number(m, e + IOUAmount::minExponent - 1);
if (m && (x.exponent() > IOUAmount::maxExponent))
return; // invalid number
issue_ = IOU;
}
@@ -871,6 +874,10 @@ public:
good_ = true;
}
Number2() : Number(), good_(true), issue_(IOU)
{
}
Number2(int64_t x) : Number(x), good_(true), issue_(IOU)
{
}
@@ -904,6 +911,18 @@ public:
return good_;
}
Issue
getIssue() const
{
return issue_;
}
void
setIssue(Issue i)
{
issue_ = i;
}
Expected<Bytes, HostFunctionError>
toBytes() const
{
@@ -917,18 +936,24 @@ public:
{
if (exponent() != std::numeric_limits<int>::lowest())
return Unexpected(
HostFunctionError::FLOAT_COMPUTATION_ERROR);
HostFunctionError::
FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE
}
else if (exponent() > 80 || exponent() < -96)
else if (exponent() > IOUAmount::maxExponent)
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
else if (exponent() < IOUAmount::minExponent)
return Number2().toBytes();
uint64_t absM = mantissa() >= 0 ? mantissa() : -mantissa();
if (absM > ((1ull << 54) - 1))
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
return Unexpected(
HostFunctionError::
FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE
v |= absM;
int const e = (!mantissa() ? 0 : exponent()) + 97;
int const e =
(!mantissa() ? 0 : exponent()) - IOUAmount::minExponent + 1;
v |= ((uint64_t)e) << 54;
}
else if (issue_ == MPT)
@@ -950,6 +975,16 @@ public:
Serializer msg;
msg.add64(v);
auto const data = msg.getData();
#ifdef DEBUG_OUTPUT
std::cout << "m: " << std::setw(20) << mantissa()
<< ", e: " << std::setw(12) << exponent() << ", hex: ";
std::cout << std::hex << std::uppercase << std::setfill('0');
for (auto const& c : data)
std::cout << std::setw(2) << (unsigned)c << " ";
std::cout << std::dec << std::setfill(' ') << std::endl;
#endif
return data;
}
@@ -958,7 +993,7 @@ protected:
toUInt(unsigned bits) const
{
if (bits >= sizeof(uint64_t) * 8)
return std::numeric_limits<uint64_t>::max();
return std::numeric_limits<uint64_t>::max(); // LCOV_EXCL_LINE
uint64_t maxV = (1ull << bits) - 1;
uint64_t absM = mantissa() >= 0 ? mantissa() : -mantissa();
@@ -969,8 +1004,9 @@ protected:
{
for (int i = 0; i > exponent(); --i)
{
// underflow
if (absM < 10)
return std::numeric_limits<uint64_t>::max(); // underflow
return std::numeric_limits<uint64_t>::max();
absM /= 10;
}
}
@@ -978,8 +1014,9 @@ protected:
{
for (int i = 0; i < exponent(); ++i)
{
// overflow
if (absM > maxV / 10)
return std::numeric_limits<uint64_t>::max(); // overflow
return std::numeric_limits<uint64_t>::max();
absM *= 10;
}
}
@@ -1041,15 +1078,14 @@ floatFromIntImpl(int64_t x, int32_t mode)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 num(x);
if (!num)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
return num.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1062,15 +1098,14 @@ floatFromUintImpl(uint64_t x, int32_t mode)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 num(x);
if (!num)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
return num.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1082,8 +1117,6 @@ floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 num(mantissa, exponent);
if (!num)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
return num.toBytes();
}
catch (...)
@@ -1105,10 +1138,12 @@ floatCompareImpl(Slice const& x, Slice const& y)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
return xx < yy ? 2 : (xx == yy ? 0 : 1);
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1127,12 +1162,15 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode)
if (!yy)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 res = xx + yy;
res.setIssue(xx.getIssue());
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1150,12 +1188,15 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode)
if (!yy)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 res = xx - yy;
res.setIssue(xx.getIssue());
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1173,12 +1214,15 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode)
if (!yy)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 res = xx * yy;
res.setIssue(xx.getIssue());
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1196,6 +1240,7 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode)
if (!yy)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 res = xx / yy;
res.setIssue(xx.getIssue());
return res.toBytes();
}
catch (...)
@@ -1209,6 +1254,9 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode)
{
try
{
if (n < 1)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
SetRound rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
@@ -1216,14 +1264,17 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode)
Number2 xx(x);
if (!xx)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 const res(root(xx, n));
Number2 res(root(xx, n));
res.setIssue(xx.getIssue());
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
@@ -1231,6 +1282,9 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
{
try
{
if (n < 0)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
SetRound rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
@@ -1241,8 +1295,8 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
if (xx == Number() && !n)
return Unexpected(HostFunctionError::INVALID_PARAMS);
Number2 const res(power(xx, n, 1));
Number2 res(power(xx, n, 1));
res.setIssue(xx.getIssue());
return res.toBytes();
}
catch (...)
@@ -1263,14 +1317,17 @@ floatLogImpl(Slice const& x, int32_t mode)
Number2 xx(x);
if (!xx)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
Number2 const res(lg(xx));
Number2 res(lg(xx));
res.setIssue(xx.getIssue());
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
}
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
// LCOV_EXCL_STOP
}
} // namespace ripple