refactor: Remove WasmNumber class (#7833)

This commit is contained in:
Mayukha Vadari
2026-07-22 15:56:33 -04:00
committed by GitHub
parent bc892f0bd3
commit 1c4abe8c5e
2 changed files with 93 additions and 143 deletions

View File

@@ -392,6 +392,20 @@ public:
static RoundingMode
setround(RoundingMode inMode);
/** Convert an integer to a RoundingMode, validating that it is in range.
*
* Returns std::nullopt if the value does not correspond to a valid
* RoundingMode.
*/
static std::optional<RoundingMode>
checkedRoundingMode(int mode) noexcept
{
if (mode < static_cast<int>(RoundingMode::ToNearest) ||
mode > static_cast<int>(RoundingMode::Upward))
return std::nullopt;
return static_cast<RoundingMode>(mode);
}
/** Returns which mantissa scale is currently in use for normalization.
*
* If you think you need to call this outside of unit tests, no you don't.

View File

@@ -12,6 +12,7 @@
#include <cstdint>
#include <iterator>
#include <optional>
#include <string>
#include <utility>
@@ -25,114 +26,75 @@ namespace wasm_float {
namespace detail {
class WasmNumber : public Number
// Decode a serialized STNumber float payload. Returns nullopt if the data is
// not a well-formed encoding.
std::optional<Number>
floatDecode(Slice const& data)
{
protected:
static unsigned constexpr encodedFloatSize = 12;
bool good_ = false;
public:
WasmNumber(Slice const& data)
if (data.size() != encodedFloatSize)
return std::nullopt;
try
{
if (data.size() != encodedFloatSize)
return;
try
{
SerialIter it(data);
Number const x = STNumber(it, sfNumber).value();
*static_cast<Number*>(this) = x;
}
catch (...)
{
return;
}
good_ = true;
SerialIter it(data);
return STNumber(it, sfNumber).value();
}
WasmNumber(Number const& n) : WasmNumber(n.mantissa(), n.exponent()) // ensure Number canonized
catch (...)
{
return std::nullopt;
}
}
template <class T>
WasmNumber(T mantissa = 0, int32_t exponent = 0)
// Build a Number from a raw mantissa/exponent pair. Returns nullopt if the
// value cannot be represented, e.g. the exponent is out of range.
std::optional<Number>
numberFromMantExp(int64_t mantissa, int32_t exponent)
{
try
{
try
{
Number n;
if constexpr (std::is_signed_v<T>)
{
n = Number(static_cast<int64_t>(mantissa), exponent);
}
else
{
n = Number(static_cast<uint64_t>(mantissa), exponent, Number::Normalized{});
}
*static_cast<Number*>(this) = n;
}
catch (...)
{
return;
}
good_ = true;
return Number(mantissa, exponent);
}
WasmNumber&
operator=(WasmNumber const&) = default;
explicit
operator bool() const
catch (...)
{
return good_;
return std::nullopt;
}
}
explicit
operator int64_t() const
{
return Number::operator int64_t();
}
Expected<Bytes, HostFunctionError>
toBytes() const
{
Serializer msg;
STNumber(sfNumber, *this).add(msg);
auto data = msg.getData();
// Serialize a Number to the STNumber float encoding.
Expected<Bytes, HostFunctionError>
floatEncode(Number const& n)
{
Serializer msg;
STNumber(sfNumber, n).add(msg);
auto 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;
std::cout << "m: " << std::setw(20) << n.mantissa() << ", e: " << std::setw(12) << n.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 Expected<Bytes, HostFunctionError>(std::move(data));
}
};
return Expected<Bytes, HostFunctionError>(std::move(data));
}
struct FloatState
{
Number::RoundingMode oldMode;
bool good = false;
// Set only when the requested mode is valid; sets the rounding mode on
// construction and restores the previous mode on destruction.
std::optional<NumberRoundModeGuard> guard;
FloatState(int32_t mode) : oldMode(Number::getround())
explicit FloatState(int32_t mode)
{
if (mode < static_cast<int32_t>(Number::RoundingMode::ToNearest) ||
mode > static_cast<int32_t>(Number::RoundingMode::Upward))
return;
Number::setround(static_cast<Number::RoundingMode>(mode));
good = true;
}
~FloatState()
{
Number::setround(oldMode);
if (auto const rm = Number::checkedRoundingMode(mode))
guard.emplace(*rm);
}
explicit
operator bool() const
{
return good;
return guard.has_value();
}
};
@@ -143,7 +105,7 @@ floatToString(Slice const& data)
{
// set default mode as we don't expect it will be used here
detail::FloatState const rm(static_cast<int32_t>(Number::RoundingMode::ToNearest));
detail::WasmNumber const num(data);
auto const num = detail::floatDecode(data);
if (!num)
{
std::string hex;
@@ -151,8 +113,7 @@ floatToString(Slice const& data)
boost::algorithm::hex(data.begin(), data.end(), std::back_inserter(hex));
return "Invalid data: " + hex;
}
auto const s = to_string(num);
return s;
return to_string(*num);
}
Expected<Bytes, HostFunctionError>
@@ -164,11 +125,7 @@ floatFromIntImpl(int64_t x, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
return detail::floatEncode(Number(x));
}
// LCOV_EXCL_START
catch (...)
@@ -187,11 +144,7 @@ floatFromUintImpl(uint64_t x, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
return detail::floatEncode(Number(x, 0, Number::Normalized{}));
}
// LCOV_EXCL_START
catch (...)
@@ -210,11 +163,7 @@ floatFromSTAmountImpl(STAmount const& x, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(static_cast<Number>(x));
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
return detail::floatEncode(static_cast<Number>(x));
}
// LCOV_EXCL_START
catch (...)
@@ -233,11 +182,7 @@ floatFromSTNumberImpl(STNumber const& x, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x.value());
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
auto const r = num.toBytes();
return r;
return detail::floatEncode(x.value());
}
// LCOV_EXCL_START
catch (...)
@@ -256,11 +201,10 @@ floatToIntImpl(Slice const& x, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
auto const num = detail::floatDecode(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
int64_t const r(num);
return r;
return static_cast<int64_t>(*num);
}
// LCOV_EXCL_START
catch (...)
@@ -279,11 +223,11 @@ floatToMantExpImpl(Slice const& x)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(x);
auto const num = detail::floatDecode(x);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed); // LCOV_EXCL_LINE
return FloatPair(num.mantissa(), num.exponent());
return FloatPair(num->mantissa(), num->exponent());
}
// LCOV_EXCL_START
catch (...)
@@ -301,10 +245,10 @@ floatFromMantExpImpl(int64_t mantissa, int32_t exponent, int32_t mode)
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const num(mantissa, exponent);
auto const num = detail::numberFromMantExp(mantissa, exponent);
if (!num)
return Unexpected(HostFunctionError::FloatInputMalformed);
return num.toBytes();
return detail::floatEncode(*num);
}
catch (...)
{
@@ -320,15 +264,15 @@ floatCompareImpl(Slice const& x, Slice const& y)
// set default mode as we don't expect it will be used here
detail::FloatState const rm(static_cast<int32_t>(Number::RoundingMode::ToNearest));
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
auto const yy = detail::floatDecode(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
if (xx < yy)
if (*xx < *yy)
return 2;
if (xx == yy)
if (*xx == *yy)
return 0;
return 1;
}
@@ -349,15 +293,14 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
auto const yy = detail::floatDecode(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx + yy;
return res.toBytes();
return detail::floatEncode(*xx + *yy);
}
// LCOV_EXCL_START
catch (...)
@@ -375,15 +318,14 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode)
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
auto const yy = detail::floatDecode(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx - yy;
return res.toBytes();
return detail::floatEncode(*xx - *yy);
}
// LCOV_EXCL_START
catch (...)
@@ -401,15 +343,14 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode)
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
auto const yy = detail::floatDecode(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx * yy;
return res.toBytes();
return detail::floatEncode(*xx * *yy);
}
// LCOV_EXCL_START
catch (...)
@@ -427,15 +368,14 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode)
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const yy(y);
auto const yy = detail::floatDecode(y);
if (!yy)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res = xx / yy;
return res.toBytes();
return detail::floatEncode(*xx / *yy);
}
catch (...)
{
@@ -455,13 +395,11 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const res(root(xx, n));
return res.toBytes();
return detail::floatEncode(root(*xx, n));
}
// LCOV_EXCL_START
catch (...)
@@ -483,15 +421,13 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
if (!rm)
return Unexpected(HostFunctionError::FloatInputMalformed);
detail::WasmNumber const xx(x);
auto const xx = detail::floatDecode(x);
if (!xx)
return Unexpected(HostFunctionError::FloatInputMalformed);
if (xx == Number() && (n == 0))
if (*xx == Number() && (n == 0))
return Unexpected(HostFunctionError::InvalidParams);
detail::WasmNumber const res(power(xx, n, 1));
return res.toBytes();
return detail::floatEncode(power(*xx, n, 1));
}
// LCOV_EXCL_START
catch (...)