From d2641d85bd6816e5bd3714acc1a6568cda99470b Mon Sep 17 00:00:00 2001 From: Olek <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:19:19 -0400 Subject: [PATCH] New floats format, STAmount compatible (#6600) --- cspell.config.yaml | 1 + include/xrpl/basics/Number.h | 3 + include/xrpl/tx/wasm/HostFunc.h | 56 + include/xrpl/tx/wasm/HostFuncImpl.h | 28 +- include/xrpl/tx/wasm/HostFuncWrapper.h | 25 + src/libxrpl/basics/Number.cpp | 15 +- src/libxrpl/tx/wasm/HostFuncImplFloat.cpp | 402 ++++--- src/libxrpl/tx/wasm/HostFuncImplGetter.cpp | 1 + src/libxrpl/tx/wasm/HostFuncWrapper.cpp | 221 +++- src/libxrpl/tx/wasm/WasmVM.cpp | 6 + src/test/app/HostFuncImpl_test.cpp | 695 +++++++++--- src/test/app/TestHostFunctions.h | 36 + src/test/app/Wasm_test.cpp | 6 +- src/test/app/wasm_fixtures/fixtures.cpp | 364 ++++-- src/test/app/wasm_fixtures/float_0/Cargo.lock | 171 +++ src/test/app/wasm_fixtures/float_0/Cargo.toml | 21 + src/test/app/wasm_fixtures/float_0/src/lib.rs | 57 + .../app/wasm_fixtures/float_tests/src/lib.rs | 1004 +++++++++++++++-- src/test/basics/Number_test.cpp | 16 +- 19 files changed, 2566 insertions(+), 562 deletions(-) create mode 100644 src/test/app/wasm_fixtures/float_0/Cargo.lock create mode 100644 src/test/app/wasm_fixtures/float_0/Cargo.toml create mode 100644 src/test/app/wasm_fixtures/float_0/src/lib.rs diff --git a/cspell.config.yaml b/cspell.config.yaml index 6eef313efb..e5775dd7ad 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -249,6 +249,7 @@ words: - STATSDCOLLECTOR - stissue - stnum + - stnumber - stobj - stobject - stpath diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index e2efcdfbef..21fe6a5b3a 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -381,6 +381,9 @@ public: friend Number root2(Number f); + friend Number + log10(Number const&, int); + // Thread local rounding control. Default is to_nearest enum rounding_mode { to_nearest, towards_zero, downward, upward }; static rounding_mode diff --git a/include/xrpl/tx/wasm/HostFunc.h b/include/xrpl/tx/wasm/HostFunc.h index a3a26cfbaf..d08ca6c1b0 100644 --- a/include/xrpl/tx/wasm/HostFunc.h +++ b/include/xrpl/tx/wasm/HostFunc.h @@ -37,6 +37,8 @@ enum class HostFunctionError : int32_t { OUT_OF_GAS = -22, }; +using FloatPair = std::pair; + inline int32_t HfErrorToInt(HostFunctionError e) { @@ -54,6 +56,24 @@ floatFromIntImpl(int64_t x, int32_t mode); Expected floatFromUintImpl(uint64_t x, int32_t mode); +Expected +floatFromSTAmountImpl(STAmount const& x, int32_t mode); + +Expected +floatFromSTNumberImpl(STNumber const& x, int32_t mode); + +Expected +floatToIntImpl(Slice const& x, int32_t mode); + +Expected +floatToMantissaAndExponentImpl(Slice const& x); + +Expected +floatNegateImpl(Slice const& x); + +Expected +floatAbsImpl(Slice const& x); + Expected floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode); @@ -440,6 +460,42 @@ struct HostFunctions return Unexpected(HostFunctionError::INTERNAL); } + virtual Expected + floatFromSTAmount(STAmount const& x, int32_t mode) const + { + return Unexpected(HostFunctionError::INTERNAL); + } + + virtual Expected + floatFromSTNumber(STNumber const& x, int32_t mode) const + { + return Unexpected(HostFunctionError::INTERNAL); + } + + virtual Expected + floatToInt(Slice const& x, int32_t mode) const + { + return Unexpected(HostFunctionError::INTERNAL); + } + + virtual Expected + floatToMantissaAndExponent(Slice const& x) const + { + return Unexpected(HostFunctionError::INTERNAL); + } + + virtual Expected + floatNegate(Slice const& x) const + { + return Unexpected(HostFunctionError::INTERNAL); + } + + virtual Expected + floatAbs(Slice const& x) const + { + return Unexpected(HostFunctionError::INTERNAL); + } + virtual Expected floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const { diff --git a/include/xrpl/tx/wasm/HostFuncImpl.h b/include/xrpl/tx/wasm/HostFuncImpl.h index 6992e1f523..dac5138416 100644 --- a/include/xrpl/tx/wasm/HostFuncImpl.h +++ b/include/xrpl/tx/wasm/HostFuncImpl.h @@ -254,6 +254,24 @@ public: Expected floatFromUint(uint64_t x, int32_t mode) const override; + Expected + floatFromSTAmount(STAmount const& x, int32_t mode) const override; + + Expected + floatFromSTNumber(STNumber const& x, int32_t mode) const override; + + Expected + floatToInt(Slice const& x, int32_t mode) const override; + + Expected + floatToMantissaAndExponent(Slice const& x) const override; + + Expected + floatNegate(Slice const& x) const override; + + Expected + floatAbs(Slice const& x) const override; + Expected floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override; @@ -282,14 +300,4 @@ public: floatLog(Slice const& x, int32_t mode) const override; }; -namespace wasm_float { - -// The range for the mantissa and exponent when normalized -static std::int64_t constexpr wasmMinMantissa = 1'000'000'000'000'000ll; -static std::int64_t constexpr wasmMaxMantissa = wasmMinMantissa * 10 - 1; -static int constexpr wasmMinExponent = -96; -static int constexpr wasmMaxExponent = 80; - -} // namespace wasm_float - } // namespace xrpl diff --git a/include/xrpl/tx/wasm/HostFuncWrapper.h b/include/xrpl/tx/wasm/HostFuncWrapper.h index 444c3be97c..c86b9673c7 100644 --- a/include/xrpl/tx/wasm/HostFuncWrapper.h +++ b/include/xrpl/tx/wasm/HostFuncWrapper.h @@ -260,6 +260,31 @@ using floatFromUint_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, wasm_trap_t* floatFromUint_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); +using floatFromSTAmount_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +wasm_trap_t* +floatFromSTAmount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +using floatFromSTNumber_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +wasm_trap_t* +floatFromSTNumber_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +using floatToInt_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t); +wasm_trap_t* +floatToInt_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +using floatToMantissaAndExponent_proto = + int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, uint8_t*, int32_t); +wasm_trap_t* +floatToMantissaAndExponent_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +using floatNegate_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t); +wasm_trap_t* +floatNegate_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +using floatAbs_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t); +wasm_trap_t* +floatAbs_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + using floatSet_proto = int32_t(int32_t, int64_t, uint8_t*, int32_t, int32_t); wasm_trap_t* floatSet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); diff --git a/src/libxrpl/basics/Number.cpp b/src/libxrpl/basics/Number.cpp index dbdb40e94f..e714f6ac91 100644 --- a/src/libxrpl/basics/Number.cpp +++ b/src/libxrpl/basics/Number.cpp @@ -949,10 +949,10 @@ power(Number const& f, unsigned n) static Number ln(Number const& x, int iterations = 50) { - static Number const N0(0); - static Number const N2(2, 0); - static Number const N05(5, -1); - static Number const LN2(693'147'180'559'945'309ll, -18); + Number const N0(0); + Number const N2(2, 0); + Number const N05(5, -1); + Number const LN2(693'147'180'559'945'309ll, -18); if (x <= 0) { @@ -993,8 +993,8 @@ ln(Number const& x, int iterations = 50) Number log10(Number const& x, int iterations) { - static Number const N0(0); - static Number const LN10(2'302'585'092'994'046ll, -15); + Number const N0(0); + Number const LN10(2'302'585'092'994'045'684ll, -18); if (x <= 0) { @@ -1012,8 +1012,9 @@ log10(Number const& x, int iterations) } // (1 <= normalX < 10) + // x = normalX * 10^norm // ln(x) = ln(normalX * 10^norm) = ln(normalX) + norm * ln(10) - int const diffExp = 15 + x.exponent(); + int const diffExp = Number::mantissaLog() + x.exponent_; Number const normalX = x / Number(1, diffExp); auto const lnX = ln(normalX, iterations) + diffExp * LN10; auto const lgX = lnX / LN10; diff --git a/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp b/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp index 1faaeab805..234bfec443 100644 --- a/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplFloat.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include @@ -12,137 +14,78 @@ namespace wasm_float { namespace detail { -class Number2 : public Number +class WasmNumber : public Number { protected: - static Bytes const floatNull; - static unsigned constexpr encodedFloatSize = 8; - static int32_t constexpr encodedMantissaBits = 54; - static int32_t constexpr encodedExponentBits = 8; - - static_assert(wasmMinExponent < 0); - - static uint64_t constexpr maxEncodedMantissa = (1ull << (encodedMantissaBits + 1)) - 1; - - bool good_; + static unsigned constexpr encodedFloatSize = 12; + bool good_ = false; public: - Number2(Slice const& data) : good_(false) + WasmNumber(Slice const& data) { if (data.size() != encodedFloatSize) return; - - if (std::ranges::equal(floatNull, data)) + try + { + SerialIter it(data); + Number const x = STNumber(it, sfNumber).value(); + *static_cast(this) = x; + } + catch (...) { - good_ = true; return; } - uint64_t const v = SerialIter(data).get64(); - if ((v & STAmount::cIssuedCurrency) == 0u) - return; - - int32_t const e = static_cast((v >> encodedMantissaBits) & 0xFFull); - int32_t const decodedExponent = e + wasmMinExponent - 1; // e - 97 - if (decodedExponent < wasmMinExponent || decodedExponent > wasmMaxExponent) - return; - - int64_t const neg = ((v & STAmount::cPositive) != 0u) ? 1 : -1; - int64_t const m = neg * static_cast(v & ((1ull << encodedMantissaBits) - 1)); - if (m == 0) - return; - - Number const x(makeNumber(m, decodedExponent)); - if (m != x.mantissa() || decodedExponent != x.exponent()) - return; // not canonical - *static_cast(this) = x; - good_ = true; } + WasmNumber(Number const& n) : WasmNumber(n.mantissa(), n.exponent()) // ensure Number canonized + { + } + template - Number2(T mantissa = 0, int32_t exponent = 0) : Number(), good_(false) + WasmNumber(T mantissa = 0, int32_t exponent = 0) { - if (!mantissa) + try + { + Number n; + if constexpr (std::is_signed_v) + { + n = Number(static_cast(mantissa), exponent); + } + else + { + n = Number(static_cast(mantissa), exponent, Number::normalized()); + } + *static_cast(this) = n; + } + catch (...) { - good_ = true; return; } - - auto const n = makeNumber(mantissa, exponent); - auto const e = n.exponent(); - if (e < wasmMinExponent) - { - good_ = true; // value is zero(as in Numbers behavior) - return; - } - - if (e > wasmMaxExponent) - return; - - *static_cast(this) = n; good_ = true; } - Number2(Number const& n) : Number2(n.mantissa(), n.exponent()) // ensure Number canonized - { - } - - static Number - makeNumber(int64_t mantissa, int32_t exponent) - { - if (mantissa < 0) - return Number(true, -static_cast(mantissa), exponent, Number::normalized()); - return Number(false, mantissa, exponent, Number::normalized()); - } - - static Number - makeNumber(uint64_t mantissa, int32_t exponent) - { - return Number(false, mantissa, exponent, Number::normalized()); - } + WasmNumber& + operator=(WasmNumber const&) = default; + explicit operator bool() const { return good_; } + explicit + operator int64_t() const + { + return Number::operator int64_t(); + } + Expected toBytes() const { - if (!good_) - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); - - auto const m = mantissa(); - auto const e = exponent(); - - uint64_t v = m >= 0 ? STAmount::cPositive : 0; - v |= STAmount::cIssuedCurrency; - - uint64_t const absM = std::abs(m); - if (absM == 0u) - { - return floatNull; - } - if (absM > maxEncodedMantissa) - { - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_LINE - } - v |= absM; - - if (e > wasmMaxExponent) - { - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); - } - if (e < wasmMinExponent) - { - return floatNull; - } - uint64_t const normExp = e - wasmMinExponent + 1; //+97 - v |= normExp << encodedMantissaBits; - Serializer msg; - msg.add64(v); + STNumber(sfNumber, *this).add(msg); auto data = msg.getData(); #ifdef DEBUG_OUTPUT @@ -153,33 +96,26 @@ public: std::cout << std::setw(2) << (unsigned)c << " "; std::cout << std::dec << std::setfill(' ') << std::endl; #endif - return data; } }; -Bytes const Number2::floatNull = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - struct FloatState { Number::rounding_mode oldMode_; - MantissaRange::mantissa_scale oldScale_; - bool good_{false}; + bool good_ = false; - FloatState(int32_t mode) : oldMode_(Number::getround()), oldScale_(Number::getMantissaScale()) + FloatState(int32_t mode) : oldMode_(Number::getround()) { if (mode < Number::rounding_mode::to_nearest || mode > Number::rounding_mode::upward) return; - Number::setround(static_cast(mode)); - Number::setMantissaScale(MantissaRange::mantissa_scale::small); good_ = true; } ~FloatState() { Number::setround(oldMode_); - Number::setMantissaScale(oldScale_); } operator bool() const @@ -195,7 +131,7 @@ floatToString(Slice const& data) { // set default mode as we don't expect it will be used here detail::FloatState const rm(Number::rounding_mode::to_nearest); - detail::Number2 const num(data); + detail::WasmNumber const num(data); if (!num) { std::string hex; @@ -203,7 +139,6 @@ 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; } @@ -217,15 +152,17 @@ floatFromIntImpl(int64_t x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const num(x); - return num.toBytes(); + detail::WasmNumber const num(x); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); // LCOV_EXCL_LINE + auto const r = num.toBytes(); + return r; } // LCOV_EXCL_START catch (...) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -238,8 +175,10 @@ floatFromUintImpl(uint64_t x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const num(x); - auto r = num.toBytes(); + detail::WasmNumber const num(x); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); // LCOV_EXCL_LINE + auto const r = num.toBytes(); return r; } // LCOV_EXCL_START @@ -247,7 +186,146 @@ floatFromUintImpl(uint64_t x, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + // LCOV_EXCL_STOP +} + +Expected +floatFromSTAmountImpl(STAmount const& x, int32_t mode) +{ + try + { + detail::FloatState const rm(mode); + if (!rm) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const num(static_cast(x)); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); // LCOV_EXCL_LINE + auto const r = num.toBytes(); + return r; + } + // LCOV_EXCL_START + catch (...) + { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + // LCOV_EXCL_STOP +} + +Expected +floatFromSTNumberImpl(STNumber const& x, int32_t mode) +{ + try + { + detail::FloatState const rm(mode); + if (!rm) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const num(x.value()); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); // LCOV_EXCL_LINE + auto const r = num.toBytes(); + return r; + } + // LCOV_EXCL_START + catch (...) + { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + // LCOV_EXCL_STOP +} + +Expected +floatToIntImpl(Slice const& x, int32_t mode) +{ + try + { + detail::FloatState const rm(mode); + if (!rm) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const num(x); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); // LCOV_EXCL_LINE + int64_t const r(num); + return r; + } + // LCOV_EXCL_START + catch (...) + { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + // LCOV_EXCL_STOP +} + +Expected +floatToMantissaAndExponentImpl(Slice const& x) +{ + try + { + detail::FloatState const rm(Number::rounding_mode::to_nearest); + if (!rm) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const num(x); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); // LCOV_EXCL_LINE + + return FloatPair(num.mantissa(), num.exponent()); + } + // LCOV_EXCL_START + catch (...) + { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + // LCOV_EXCL_STOP +} + +Expected +floatNegateImpl(Slice const& x) +{ + try + { + detail::FloatState const rm(Number::rounding_mode::to_nearest); + if (!rm) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const num(x); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const res = -num; + return res.toBytes(); + } + // LCOV_EXCL_START + catch (...) + { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + // LCOV_EXCL_STOP +} + +Expected +floatAbsImpl(Slice const& x) +{ + try + { + detail::FloatState const rm(Number::rounding_mode::to_nearest); + if (!rm) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const num(x); + if (!num) + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); + + detail::WasmNumber const res = abs(num); + return res.toBytes(); + } + // LCOV_EXCL_START + catch (...) + { + return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + } // LCOV_EXCL_STOP } @@ -259,16 +337,15 @@ floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const num(mantissa, exponent); + detail::WasmNumber const num(mantissa, exponent); if (!num) - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); + return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); return num.toBytes(); } catch (...) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } Expected @@ -278,10 +355,11 @@ floatCompareImpl(Slice const& x, Slice const& y) { // set default mode as we don't expect it will be used here detail::FloatState const rm(Number::rounding_mode::to_nearest); - detail::Number2 const xx(x); + + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const yy(y); + detail::WasmNumber const yy(y); if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); if (xx < yy) @@ -295,7 +373,6 @@ floatCompareImpl(Slice const& x, Slice const& y) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -308,13 +385,13 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const yy(y); + detail::WasmNumber const yy(y); if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const res = xx + yy; + detail::WasmNumber const res = xx + yy; return res.toBytes(); } @@ -323,7 +400,6 @@ floatAddImpl(Slice const& x, Slice const& y, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -335,13 +411,13 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const yy(y); + detail::WasmNumber const yy(y); if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const res = xx - yy; + detail::WasmNumber const res = xx - yy; return res.toBytes(); } @@ -350,7 +426,6 @@ floatSubtractImpl(Slice const& x, Slice const& y, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -362,13 +437,13 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const yy(y); + detail::WasmNumber const yy(y); if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const res = xx * yy; + detail::WasmNumber const res = xx * yy; return res.toBytes(); } @@ -377,7 +452,6 @@ floatMultiplyImpl(Slice const& x, Slice const& y, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -389,13 +463,13 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode) detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const yy(y); + detail::WasmNumber const yy(y); if (!yy) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const res = xx / yy; + detail::WasmNumber const res = xx / yy; return res.toBytes(); } @@ -403,7 +477,6 @@ floatDivideImpl(Slice const& x, Slice const& y, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } Expected @@ -418,11 +491,11 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const res(root(xx, n)); + detail::WasmNumber const res(root(xx, n)); return res.toBytes(); } @@ -431,7 +504,6 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -440,20 +512,20 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode) { try { - if ((n < 0) || (n > wasmMaxExponent)) + if ((n < 0) || (n > Number::maxExponent)) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); detail::FloatState const rm(mode); if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); if (xx == Number() && (n == 0)) return Unexpected(HostFunctionError::INVALID_PARAMS); - detail::Number2 const res(power(xx, n, 1)); + detail::WasmNumber const res(power(xx, n, 1)); return res.toBytes(); } @@ -462,7 +534,6 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -475,11 +546,11 @@ floatLogImpl(Slice const& x, int32_t mode) if (!rm) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const xx(x); + detail::WasmNumber const xx(x); if (!xx) return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED); - detail::Number2 const res(log10(xx)); + detail::WasmNumber const res(log10(xx)); return res.toBytes(); } @@ -488,7 +559,6 @@ floatLogImpl(Slice const& x, int32_t mode) { return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); } - return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR); // LCOV_EXCL_STOP } @@ -510,6 +580,42 @@ WasmHostFunctionsImpl::floatFromUint(uint64_t x, int32_t mode) const return wasm_float::floatFromUintImpl(x, mode); } +Expected +WasmHostFunctionsImpl::floatFromSTAmount(STAmount const& x, int32_t mode) const +{ + return wasm_float::floatFromSTAmountImpl(x, mode); +} + +Expected +WasmHostFunctionsImpl::floatFromSTNumber(STNumber const& x, int32_t mode) const +{ + return wasm_float::floatFromSTNumberImpl(x, mode); +} + +Expected +WasmHostFunctionsImpl::floatToInt(Slice const& x, int32_t mode) const +{ + return wasm_float::floatToIntImpl(x, mode); +} + +Expected +WasmHostFunctionsImpl::floatToMantissaAndExponent(Slice const& x) const +{ + return wasm_float::floatToMantissaAndExponentImpl(x); +} + +Expected +WasmHostFunctionsImpl::floatNegate(Slice const& x) const +{ + return wasm_float::floatNegateImpl(x); +} + +Expected +WasmHostFunctionsImpl::floatAbs(Slice const& x) const +{ + return wasm_float::floatAbsImpl(x); +} + Expected WasmHostFunctionsImpl::floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const { diff --git a/src/libxrpl/tx/wasm/HostFuncImplGetter.cpp b/src/libxrpl/tx/wasm/HostFuncImplGetter.cpp index 46744c17a7..a83217b507 100644 --- a/src/libxrpl/tx/wasm/HostFuncImplGetter.cpp +++ b/src/libxrpl/tx/wasm/HostFuncImplGetter.cpp @@ -90,6 +90,7 @@ getAnyFieldData(STBase const* obj) } case STI_AMOUNT: + case STI_NUMBER: default: break; // Use serializer } diff --git a/src/libxrpl/tx/wasm/HostFuncWrapper.cpp b/src/libxrpl/tx/wasm/HostFuncWrapper.cpp index e420565727..bbad5e1e1a 100644 --- a/src/libxrpl/tx/wasm/HostFuncWrapper.cpp +++ b/src/libxrpl/tx/wasm/HostFuncWrapper.cpp @@ -47,7 +47,7 @@ setData( } template -Expected +static Expected getDataInt32(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i) { auto const result = params->data[i].of.i32; @@ -56,7 +56,7 @@ getDataInt32(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i) } template -Expected +static Expected getDataInt64(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i) { auto const result = params->data[i].of.i64; @@ -65,7 +65,7 @@ getDataInt64(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i) } template -Expected +static Expected getDataUnsigned(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) { static_assert(std::is_unsigned_v); @@ -91,21 +91,21 @@ getDataUnsigned(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) } template -Expected +static Expected getDataUInt32(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) { return getDataUnsigned(runtime, params, i); } template -Expected +static Expected getDataUInt64(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) { return getDataUnsigned(runtime, params, i); } template -Expected +static Expected getDataSField(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i) { auto const& m = SField::getKnownCodeToField(); @@ -119,7 +119,7 @@ getDataSField(IW const* _runtime, wasm_val_vec_t const* params, int32_t& i) } template -Expected +static Expected getDataSlice(IW const* runtime, wasm_val_vec_t const* params, int32_t& i, bool isUpdate = false) { int64_t const ptr = params->data[i].of.i32; @@ -148,7 +148,7 @@ getDataSlice(IW const* runtime, wasm_val_vec_t const* params, int32_t& i, bool i } template -Expected +static Expected getDataUInt256(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) { auto const slice = getDataSlice(runtime, params, i); @@ -165,7 +165,7 @@ getDataUInt256(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) } template -Expected +static Expected getDataAccountID(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) { auto const slice = getDataSlice(runtime, params, i); @@ -240,7 +240,7 @@ getDataAsset(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) } template -Expected +static Expected getDataString(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) { auto const slice = getDataSlice(runtime, params, i); @@ -249,7 +249,7 @@ getDataString(IW const* runtime, wasm_val_vec_t const* params, int32_t& i) return std::string_view(reinterpret_cast(slice->data()), slice->size()); } -std::nullptr_t +static std::nullptr_t hfResult(wasm_val_vec_t* results, int32_t value) { results->data[0] = WASM_I32_VAL(value); @@ -257,7 +257,7 @@ hfResult(wasm_val_vec_t* results, int32_t value) return nullptr; } -std::nullptr_t +static std::nullptr_t hfResult(wasm_val_vec_t* results, HostFunctionError value) { results->data[0] = WASM_I32_VAL(HfErrorToInt(value)); @@ -266,7 +266,7 @@ hfResult(wasm_val_vec_t* results, HostFunctionError value) } template -std::nullptr_t +static std::nullptr_t returnResult( InstanceWrapper const* runtime, wasm_val_vec_t const* params, @@ -282,6 +282,9 @@ returnResult( using t = std::decay_t; if constexpr (std::is_same_v) { + if (index < 0 || index + 1 >= params->size) + return hfResult(results, HostFunctionError::INTERNAL); // LCOV_EXCL_LINE + return hfResult( results, setData( @@ -293,6 +296,9 @@ returnResult( } else if constexpr (std::is_same_v) { + if (index < 0 || index + 1 >= params->size) + return hfResult(results, HostFunctionError::INTERNAL); // LCOV_EXCL_LINE + return hfResult( results, setData( @@ -308,6 +314,9 @@ returnResult( } else if constexpr (std::is_same_v) { + if (index < 0 || index + 1 >= params->size) + return hfResult(results, HostFunctionError::INTERNAL); // LCOV_EXCL_LINE + auto const resultValue = adjustWasmEndianess(res.value()); return hfResult( results, @@ -318,6 +327,49 @@ returnResult( reinterpret_cast(&resultValue), static_cast(sizeof(resultValue)))); } + else if constexpr (std::is_same_v) + { + if (index < 0 || index + 1 >= params->size) + return hfResult(results, HostFunctionError::INTERNAL); // LCOV_EXCL_LINE + + auto const resultValue = adjustWasmEndianess(res.value()); + return hfResult( + results, + setData( + runtime, + params->data[index].of.i32, + params->data[index + 1].of.i32, + reinterpret_cast(&resultValue), + static_cast(sizeof(resultValue)))); + } + else if constexpr (std::is_same_v) + { + if (index < 0 || index + 3 >= params->size) + return hfResult(results, HostFunctionError::INTERNAL); // LCOV_EXCL_LINE + + auto const mantissa = adjustWasmEndianess(res->first); + auto const r1 = setData( + runtime, + params->data[index].of.i32, + params->data[index + 1].of.i32, + reinterpret_cast(&mantissa), + static_cast(sizeof(mantissa))); + if (r1 < 0) + return hfResult(results, r1); + + index += 2; + auto const exponent = adjustWasmEndianess(res->second); + auto const r2 = setData( + runtime, + params->data[index].of.i32, + params->data[index + 1].of.i32, + reinterpret_cast(&exponent), + static_cast(sizeof(exponent))); + if (r2 < 0) + return hfResult(results, r2); + + return hfResult(results, r1 + r2); // 12 + } else { static_assert([] { return false; }(), "Unhandled return type in returnResult"); @@ -1568,6 +1620,149 @@ floatFromUint_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* resu return returnResult(runtime, params, results, hf->floatFromUint(*x, *rounding), i); } +wasm_trap_t* +floatFromSTAmount_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + if (auto g = checkGas(env); !g) + return g.error(); // LCOV_EXCL_LINE + auto* hf = getHF(env); + auto const* runtime = reinterpret_cast(hf->getRT()); + + int i = 0; + auto const x = getDataSlice(runtime, params, i); + if (!x) + return hfResult(results, x.error()); + + auto serialIter = SerialIter(*x); + std::optional amount; + try + { + amount = STAmount(serialIter, sfGeneric); + } + catch (std::exception const&) + { + amount = std::nullopt; + } + if (!amount) + return hfResult(results, HostFunctionError::INVALID_PARAMS); + + i = 4; + auto const rounding = getDataInt32(runtime, params, i); + if (!rounding) + return hfResult(results, rounding.error()); // LCOV_EXCL_LINE + + i = 2; + return returnResult(runtime, params, results, hf->floatFromSTAmount(*amount, *rounding), i); +} + +wasm_trap_t* +floatFromSTNumber_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + if (auto g = checkGas(env); !g) + return g.error(); // LCOV_EXCL_LINE + auto* hf = getHF(env); + auto const* runtime = reinterpret_cast(hf->getRT()); + + int i = 0; + auto const x = getDataSlice(runtime, params, i); + if (!x) + return hfResult(results, x.error()); + + auto serialIter = SerialIter(*x); + std::optional num; + try + { + num = STNumber(serialIter, sfGeneric); + } + catch (std::exception const&) + { + num = std::nullopt; + } + if (!num) + return hfResult(results, HostFunctionError::INVALID_PARAMS); + + i = 4; + auto const rounding = getDataInt32(runtime, params, i); + if (!rounding) + return hfResult(results, rounding.error()); // LCOV_EXCL_LINE + + i = 2; + return returnResult(runtime, params, results, hf->floatFromSTNumber(*num, *rounding), i); +} + +wasm_trap_t* +floatToInt_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + if (auto g = checkGas(env); !g) + return g.error(); // LCOV_EXCL_LINE + auto* hf = getHF(env); + auto const* runtime = reinterpret_cast(hf->getRT()); + + int i = 0; + auto const x = getDataSlice(runtime, params, i); + if (!x) + return hfResult(results, x.error()); + + i = 4; + auto const rounding = getDataInt32(runtime, params, i); + if (!rounding) + return hfResult(results, rounding.error()); // LCOV_EXCL_LINE + + i = 2; + return returnResult(runtime, params, results, hf->floatToInt(*x, *rounding), i); +} + +wasm_trap_t* +floatToMantissaAndExponent_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + if (auto g = checkGas(env); !g) + return g.error(); // LCOV_EXCL_LINE + auto* hf = getHF(env); + auto const* runtime = reinterpret_cast(hf->getRT()); + + int i = 0; + auto const x = getDataSlice(runtime, params, i); + if (!x) + return hfResult(results, x.error()); + + i = 2; + return returnResult(runtime, params, results, hf->floatToMantissaAndExponent(*x), i); +} + +wasm_trap_t* +floatNegate_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + if (auto g = checkGas(env); !g) + return g.error(); // LCOV_EXCL_LINE + auto* hf = getHF(env); + auto const* runtime = reinterpret_cast(hf->getRT()); + + int i = 0; + auto const x = getDataSlice(runtime, params, i); + if (!x) + return hfResult(results, x.error()); + + i = 2; + return returnResult(runtime, params, results, hf->floatNegate(*x), i); +} + +wasm_trap_t* +floatAbs_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + if (auto g = checkGas(env); !g) + return g.error(); // LCOV_EXCL_LINE + auto* hf = getHF(env); + auto const* runtime = reinterpret_cast(hf->getRT()); + + int i = 0; + auto const x = getDataSlice(runtime, params, i); + if (!x) + return hfResult(results, x.error()); + + i = 2; + return returnResult(runtime, params, results, hf->floatAbs(*x), i); +} + wasm_trap_t* floatSet_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) { diff --git a/src/libxrpl/tx/wasm/WasmVM.cpp b/src/libxrpl/tx/wasm/WasmVM.cpp index 7c755da2b3..fdd9a437d7 100644 --- a/src/libxrpl/tx/wasm/WasmVM.cpp +++ b/src/libxrpl/tx/wasm/WasmVM.cpp @@ -76,6 +76,12 @@ setCommonHostFunctions(HostFunctions* hfs, ImportVec& i) WASM_IMPORT_FUNC2(i, floatFromInt, "float_from_int", hfs, 100); WASM_IMPORT_FUNC2(i, floatFromUint, "float_from_uint", hfs, 130); + WASM_IMPORT_FUNC2(i, floatFromSTAmount, "float_from_stamount", hfs, 150); + WASM_IMPORT_FUNC2(i, floatFromSTNumber, "float_from_stnumber", hfs, 150); + WASM_IMPORT_FUNC2(i, floatToInt, "float_to_int", hfs, 130); + WASM_IMPORT_FUNC2(i, floatToMantissaAndExponent, "float_to_mantissa_and_exponent", hfs, 130); + WASM_IMPORT_FUNC2(i, floatNegate, "float_negate", hfs, 150); + WASM_IMPORT_FUNC2(i, floatAbs, "float_abs", hfs, 150); WASM_IMPORT_FUNC2(i, floatSet, "float_set", hfs, 100); WASM_IMPORT_FUNC2(i, floatCompare, "float_compare", hfs, 80); WASM_IMPORT_FUNC2(i, floatAdd, "float_add", hfs, 160); diff --git a/src/test/app/HostFuncImpl_test.cpp b/src/test/app/HostFuncImpl_test.cpp index e46634ca44..e61c38f771 100644 --- a/src/test/app/HostFuncImpl_test.cpp +++ b/src/test/app/HostFuncImpl_test.cpp @@ -2065,29 +2065,90 @@ struct HostFuncImpl_test : public beast::unit_test::suite // clang-format off - int const normalExp = 15; + int const normalExp = 18; - Bytes const floatIntMin = {0x99, 0x20, 0xc4, 0x9b, 0xa5, 0xe3, 0x53, 0xf8}; // -2^63 - Bytes const floatIntZero = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 0 - Bytes const floatIntMax = {0xd9, 0x20, 0xc4, 0x9b, 0xa5, 0xe3, 0x53, 0xf8}; // 2^63-1 - Bytes const floatUIntMax = {0xd9, 0x46, 0x8d, 0xb8, 0xba, 0xc7, 0x10, 0xcb}; // 2^64-1 - Bytes const floatMaxExp = {0xEC, 0x43, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 1e(80+15) - Bytes const floatPreMaxExp = {0xEC, 0x03, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 1e(79+15) - Bytes const floatMinusMaxExp = {0xAC, 0x43, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // -1e(80+15) - Bytes const floatMaxIOU = {0xEC, 0x63, 0x86, 0xF2, 0x6F, 0xC0, 0xFF, 0xFF}; // 1e(81+15)-1 - Bytes const floatMinExp = {0xC0, 0x43, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 1e-96 - Bytes const float1 = {0xD4, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 1 - Bytes const floatMinus1 = {0x94, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // -1 - Bytes const float1More = {0xD4, 0x83, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x01}; // 1.000 000 000 000 001 - Bytes const float2 = {0xD4, 0x87, 0x1A, 0xFD, 0x49, 0x8D, 0x00, 0x00}; // 2 - Bytes const float10 = {0xD4, 0xC3, 0x8D, 0x7E, 0xA4, 0xC6, 0x80, 0x00}; // 10 - Bytes const floatInvalidZero = {0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // INVALID - Bytes const floatPi = {0xD4, 0x8B, 0x29, 0x43, 0x0A, 0x25, 0x6D, 0x21}; // 3.141592653589793 + Bytes const floatIntMin = {0xF3, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, 0x01}; // -2^63 + Bytes const floatIntZero = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; // 0 + Bytes const floatIntMax = {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; // 2^63-1 + Bytes const floatUIntMax = {0x19, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x00, 0x00, 0x00, 0x01}; // 2^64-1 + + Bytes const floatMaxExp = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00}; // 1e(Number::maxExponent + normalExp) + Bytes const floatPreMaxExp = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF}; // 1e(Number::maxExponent + normalExp - 1) + Bytes const floatMinusMaxExp = {0xF2, 0x1F, 0x49, 0x4C, 0x58, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00}; // -1e(Number::maxExponent + normalExp) + Bytes const floatMinExp = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}; // 1e(Number::minExponent - normalExp) + Bytes const floatMax = {0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x80, 0x00}; // Number::maxRep e(Number::maxExponent - normalExp) + + Bytes const floatMaxIOU = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x63, 0xFF, 0x9C, 0x00, 0x00, 0x00, 0x4E}; // 9999999999999999e(96) + Bytes const floatMinIOU = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x9D}; // 1e(-96 - 3 + normalExp = -81) + + Bytes const float1 = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // 1 + Bytes const floatMinus1 = {0xF2, 0x1F, 0x49, 0x4C, 0x58, 0x9C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // -1 + Bytes const float1More = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x03, 0xE8, 0xFF, 0xFF, 0xFF, 0xEE}; // 1.000 000 000 000 001 + Bytes const float2 = {0x1B, 0xC1, 0x6D, 0x67, 0x4E, 0xC8, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // 2 + Bytes const float10 = {0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEF}; // 10 + Bytes const floatPi = {0x2B, 0x99, 0x2D, 0xDF, 0xA2, 0x32, 0x48, 0xE8, 0xFF, 0xFF, 0xFF, 0xEE}; // 3.141592653589793 + Bytes const floatInvalidZero = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00}; // INVALID + Bytes const floatMinus3 = {0xD6, 0x5D, 0xDB, 0xE5, 0x09, 0xD4, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE}; // -3 std::string const invalid = "invalid_data"; // clang-format on + template + void + printFloats(std::string_view descr, T m, int e) + { + Serializer msg; + Number n; + + if constexpr (std::is_signed_v) + { + n = Number(static_cast(m), e); + } + else + { + n = Number(static_cast(m), e, Number::normalized()); + } + + STNumber(sfNumber, n).add(msg); + auto const& data = msg.modData(); + std::cout << std::setw(24) << descr << " m: " << std::setw(20) << n.mantissa() + << ", e: " << std::setw(8) << 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; + } + + void + printNumbersBin() + { + printFloats("int64.min", std::numeric_limits::min(), 0); + printFloats("zero", 0, 0); + printFloats("int64.max", std::numeric_limits::max(), 0); + printFloats("uint64.max", std::numeric_limits::max(), 0); + + printFloats("Number 1 max exp", 1, Number::maxExponent + normalExp); + printFloats("Number (max exp - 1)", 1, Number::maxExponent + normalExp - 1); + printFloats("Number -1 max exp", -1, Number::maxExponent + normalExp); + + printFloats("Number.max", Number::maxRep, Number::maxExponent); + printFloats("Number min positive", 1, Number::minExponent + normalExp); + printFloats( + "Number.min", std::numeric_limits::min(), Number::maxExponent - normalExp); + printFloats("STAmount.max", STAmount::cMaxValue, STAmount::cMaxOffset); + printFloats("STAmount min positive", STAmount::cMinValue, STAmount::cMinOffset); + + printFloats("one", 1, 0); + printFloats("-one", -1, 0); + printFloats("1,00...01", 1'000'000'000'000'001, -15); + printFloats("two", 2, 0); + printFloats("ten", 10, 0); + printFloats("pi", 3141592653589793, -15); + printFloats("-three", -3, 0); + return; + } + void testTraceFloat() { @@ -2236,38 +2297,38 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { - auto const result = hfs.floatSet(1, wasmMaxExponent + normalExp + 1, 0); + auto const result = hfs.floatSet(1, Number::maxExponent + normalExp + 1, 0); BEAST_EXPECT(!result) && - BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } { - auto const result = hfs.floatSet(1, wasmMinExponent + normalExp - 1, 0); + auto const result = hfs.floatSet(1, Number::minExponent + normalExp - 1, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero); } { - auto const result = hfs.floatSet(1, wasmMaxExponent + normalExp, 0); + auto const result = hfs.floatSet(1, Number::maxExponent + normalExp, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxExp); } { - auto const result = hfs.floatSet(-1, wasmMaxExponent + normalExp, 0); + auto const result = hfs.floatSet(-1, Number::maxExponent + normalExp, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinusMaxExp); } { - auto const result = hfs.floatSet(1, wasmMaxExponent + normalExp - 1, 0); + auto const result = hfs.floatSet(1, Number::maxExponent + normalExp - 1, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatPreMaxExp); } { - auto const result = hfs.floatSet(STAmount::cMaxValue, wasmMaxExponent, 0); + auto const result = hfs.floatSet(STAmount::cMaxValue, STAmount::cMaxOffset, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxIOU); } { - auto const result = hfs.floatSet(1, wasmMinExponent + normalExp, 0); + auto const result = hfs.floatSet(1, Number::minExponent - normalExp, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinExp); } @@ -2275,12 +2336,6 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const result = hfs.floatSet(10, -1, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == float1); } - - { - auto const result = hfs.floatSet(1, Number::maxExponent + normalExp + 1, 0); - BEAST_EXPECT(!result) && - BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); - } } void @@ -2312,15 +2367,6 @@ struct HostFuncImpl_test : public beast::unit_test::suite BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } - { - auto x = floatMaxExp; - // exp = 81 + 97 = 178 - x[1] |= 0x80; - x[1] &= 0xBF; - auto const result = hfs.floatCompare(makeSlice(x), makeSlice(floatMaxExp)); - BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); - } - { auto const result = hfs.floatCompare(makeSlice(floatIntMin), makeSlice(floatIntZero)); BEAST_EXPECT(result) && BEAST_EXPECT(*result == 2); @@ -2368,9 +2414,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { + // max IOU is too small to make any change auto const result = hfs.floatAdd(makeSlice(floatMaxIOU), makeSlice(floatMaxExp), 0); - BEAST_EXPECT(!result) && - BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxExp); } { @@ -2379,8 +2425,9 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { + // Number can't hold int64.min, it is rounded and we get -3, not -1 auto const result = hfs.floatAdd(makeSlice(floatIntMax), makeSlice(floatIntMin), 0); - BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinus3); } } @@ -2416,9 +2463,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const result = - hfs.floatSubtract(makeSlice(floatMaxIOU), makeSlice(floatMinusMaxExp), 0); - BEAST_EXPECT(!result) && - BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + hfs.floatSubtract(makeSlice(floatMinusMaxExp), makeSlice(floatMaxIOU), 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinusMaxExp); } { @@ -2464,7 +2510,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { - auto const result = hfs.floatMultiply(makeSlice(floatMaxIOU), makeSlice(float1More), 0); + auto const result = hfs.floatMultiply(makeSlice(floatMax), makeSlice(float1More), 0); BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } @@ -2526,7 +2572,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const y = hfs.floatSet(STAmount::cMaxValue, -normalExp - 1, 0); // 0.9999999... if (BEAST_EXPECT(y)) { - auto const result = hfs.floatDivide(makeSlice(floatMaxIOU), makeSlice(*y), 0); + auto const result = hfs.floatDivide(makeSlice(floatMax), makeSlice(*y), 0); BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } @@ -2643,23 +2689,17 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { - auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 2, 0); + auto const result = hfs.floatPower(makeSlice(floatMax), 2, 0); BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); } { - auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 81, 0); + auto const result = hfs.floatPower(makeSlice(floatMax), Number::maxExponent + 1, 0); BEAST_EXPECT(!result) && BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } - { - auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 2, 0); - BEAST_EXPECT(!result) && - BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); - } - { auto const result = hfs.floatPower(makeSlice(floatMaxIOU), 0, 0); BEAST_EXPECT(result) && BEAST_EXPECT(*result == float1); @@ -2714,48 +2754,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); } - // perf test logs - // { - // auto const result = hfs.floatLog(makeSlice(floatPi), 0); - // if (BEAST_EXPECT(result)) - // { - // std::cout << "lg(" << floatToString(makeSlice(floatPi)) - // << ") = " << floatToString(makeSlice(*result)) - // << std::endl; - // } - // } - // { - // auto const result = hfs.floatLog(makeSlice(floatIntMax), 0); - // if (BEAST_EXPECT(result)) - // { - // std::cout << "lg(" << floatToString(makeSlice(floatIntMax)) - // << ") = " << floatToString(makeSlice(*result)) - // << std::endl; - // } - // } - - // { - // auto const result = hfs.floatLog(makeSlice(floatMaxExp), 0); - // if (BEAST_EXPECT(result)) - // { - // std::cout << "lg(" << floatToString(makeSlice(floatMaxExp)) - // << ") = " << floatToString(makeSlice(*result)) - // << std::endl; - // } - // } - - // { - // auto const result = hfs.floatLog(makeSlice(floatMaxIOU), 0); - // if (BEAST_EXPECT(result)) - // { - // std::cout << "lg(" << floatToString(makeSlice(floatMaxIOU)) - // << ") = " << floatToString(makeSlice(*result)) - // << std::endl; - // } - // } - { - auto const x = hfs.floatSet(9'500'000'000'000'001, -14, 0); // almost 80+15 + auto const x = hfs.floatSet(32'786, 0, 0); if (BEAST_EXPECT(x)) { auto const result = hfs.floatLog(makeSlice(floatMaxExp), 0); @@ -2774,7 +2774,7 @@ struct HostFuncImpl_test : public beast::unit_test::suite { auto const x = hfs.floatSet(1000, 0, 0); // 1000 - auto const y = hfs.floatSet(3, 0, 0); // 0.1 + auto const y = hfs.floatSet(3, 0, 0); // 3 if (BEAST_EXPECT(x && y)) { auto const result = hfs.floatLog(makeSlice(*x), 0); @@ -2783,8 +2783,8 @@ struct HostFuncImpl_test : public beast::unit_test::suite } { - auto const x = hfs.floatSet(1, -2, 0); // 0.01 - auto const y = hfs.floatSet(-2'000'000'000'000'000ll, -15, 0); // -2 + auto const x = hfs.floatSet(1, -2, 0); // 0.01 + auto const y = hfs.floatSet(-1'999'999'999'999'999'999, -normalExp, 0); // -2 if (BEAST_EXPECT(x && y)) { auto const result = hfs.floatLog(makeSlice(*x), 0); @@ -2796,7 +2796,6 @@ struct HostFuncImpl_test : public beast::unit_test::suite void testFloatSpecialCases() { - testcase("float Xrp+Mpt"); using namespace test::jtx; Env env{*this}; @@ -2805,60 +2804,472 @@ struct HostFuncImpl_test : public beast::unit_test::suite auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); WasmHostFunctionsImpl const hfs(ac, dummyEscrow); - auto const y = hfs.floatSet(20, 0, 0); - if (!BEAST_EXPECT(y)) - return; - - Bytes x(8); - - // XRP - memset(x.data(), 0, x.size()); - x[0] = 0x40; - x[7] = 10; - - { - auto const result = hfs.floatCompare(makeSlice(x), makeSlice(float10)); - BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); - } - - { - auto const result = hfs.floatAdd(makeSlice(float10), makeSlice(x), 0); - BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); - } - - // MPT - memset(x.data(), 0, x.size()); - x[0] = 0x60; - x[7] = 10; - - { - auto const result = hfs.floatCompare(makeSlice(x), makeSlice(float10)); - BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); - } - - { - auto const result = hfs.floatAdd(makeSlice(float10), makeSlice(x), 0); - BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); - } - testcase("float non-canonical"); - { // non-canonical mantissa 10 000 000 000 000 000 - Bytes x = float1; - *reinterpret_cast(x.data()) = 0x0000C16FF286A3D4ull; + { // non-canonical mantissa 100000e-4 + Bytes const y = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0xA0, 0xFF, 0xFF, 0xFF, 0xFC}; + auto const result = hfs.floatCompare(makeSlice(y), makeSlice(float10)); + BEAST_EXPECT(result && *result == 0); + } + } + + void + testFloatFromSTAmount() + { + testcase("floatFromSTAmount"); + using namespace test::jtx; + + Env env{*this}; + OpenView ov{*env.current()}; + ApplyContext ac = createApplyContext(env, ov); + auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); + WasmHostFunctionsImpl const hfs(ac, dummyEscrow); + + { + STAmount const amount = XRP(100); + auto const result = hfs.floatFromSTAmount(amount, -1); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + STAmount const amount = XRP(100); + auto const result = hfs.floatFromSTAmount(amount, 4); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + STAmount const amount = XRP(0); + auto const result = hfs.floatFromSTAmount(amount, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero); + } + + { + STAmount const amount = XRP(-1); + auto const y = hfs.floatSet(-1 * 1'000'000, 0, 0); + if (BEAST_EXPECT(y)) { - auto const result = hfs.floatCompare(makeSlice(x), makeSlice(float1)); - BEAST_EXPECT(!result && result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + auto const result = hfs.floatFromSTAmount(amount, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == *y); } } + + { + auto const y = hfs.floatSet(9223372036854776, 3, 0); + STAmount const amount(noIssue(), std::numeric_limits::max()); + auto const result = hfs.floatFromSTAmount(amount, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == *y); + } + + { + bool ex = false; + try + { + STAmount const amount(noIssue(), -1, Number::maxExponent + normalExp); + [[maybe_unused]] auto const r = hfs.floatFromSTAmount(amount, 0); + } + catch (...) + { + ex = true; + } + + BEAST_EXPECT(ex); + } + + auto const USD = env.master["USD"]; + { + STAmount const amount( + IOUAmount(STAmount::cMinValue, STAmount::cMinOffset), USD.issue()); + auto const result = hfs.floatFromSTAmount(amount, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinIOU); + } + + { + STAmount const amount( + IOUAmount(STAmount::cMaxValue, STAmount::cMaxOffset), USD.issue()); + auto const result = hfs.floatFromSTAmount(amount, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxIOU); + } + } + + void + testFloatFromSTNumber() + { + testcase("floatFromSTNumber"); + using namespace test::jtx; + + Env env{*this}; + OpenView ov{*env.current()}; + ApplyContext ac = createApplyContext(env, ov); + auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); + WasmHostFunctionsImpl const hfs(ac, dummyEscrow); + + // Test with invalid rounding mode + { + STNumber const num(sfNumber, Number(123, 0)); + auto const result = hfs.floatFromSTNumber(num, -1); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + STNumber const num(sfNumber, Number(123, 0)); + auto const result = hfs.floatFromSTNumber(num, 4); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + STNumber const num( + sfNumber, Number(std::numeric_limits::max(), 0, Number::normalized())); + auto const result = hfs.floatFromSTNumber(num, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatUIntMax); + } + + { + STNumber const num(sfNumber, Number(-1, Number::maxExponent + normalExp)); + auto const result = hfs.floatFromSTNumber(num, 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinusMaxExp); + } + } + + void + testFloatToInt() + { + testcase("floatToInt"); + using namespace test::jtx; + + Env env{*this}; + OpenView ov{*env.current()}; + ApplyContext ac = createApplyContext(env, ov); + auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); + WasmHostFunctionsImpl const hfs(ac, dummyEscrow); + + { + auto const result = hfs.floatToInt(makeSlice(float1), -1); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatToInt(makeSlice(float1), 4); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatToInt(Slice(), 0); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatToInt(makeSlice(invalid), 0); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatToInt(makeSlice(floatIntZero), 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == 0); + + // roundtrip + auto const result2 = hfs.floatFromInt(*result, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntZero); + } + + { + auto const result = hfs.floatToInt(makeSlice(float1), 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == 1); + + // roundtrip + auto const result2 = hfs.floatFromInt(*result, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == float1); + } + + { + auto const result = hfs.floatToInt(makeSlice(floatMinus1), 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == -1); + + // roundtrip + auto const result2 = hfs.floatFromInt(*result, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatMinus1); + } + + { + auto const result = hfs.floatToInt(makeSlice(floatIntMax), 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == std::numeric_limits::max()); + + // roundtrip + auto const result2 = hfs.floatFromInt(*result, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntMax); + } + + { + // Number can't hold int64.min, it is rounded and we get int64_t.min - 3, which doesn't + // fit into int64 + auto const result = hfs.floatToInt(makeSlice(floatIntMin), 0); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + + { + auto const result = hfs.floatToInt(makeSlice(floatUIntMax), 0); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_COMPUTATION_ERROR); + } + + // Test rounding modes with pi (3.141592653589793) + { + // to_nearest (mode 0): should round to 3 + auto const result = hfs.floatToInt(makeSlice(floatPi), 0); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == 3); + } + + { + // towards_zero (mode 1): should truncate to 3 + auto const result = hfs.floatToInt(makeSlice(floatPi), 1); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == 3); + } + + { + // downward (mode 2): should round down to 3 + auto const result = hfs.floatToInt(makeSlice(floatPi), 2); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == 3); + } + + { + // upward (mode 3): should round up to 4 + auto const result = hfs.floatToInt(makeSlice(floatPi), 3); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == 4); + } + } + + void + testFloatToMantissaAndExponent() + { + testcase("floatToMantissaAndExponent"); + using namespace test::jtx; + + Env env{*this}; + OpenView ov{*env.current()}; + ApplyContext ac = createApplyContext(env, ov); + auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); + WasmHostFunctionsImpl const hfs(ac, dummyEscrow); + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(invalid)); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(floatIntZero)); + BEAST_EXPECT(result) && BEAST_EXPECT(result->first == 0) && + BEAST_EXPECT(result->second == std::numeric_limits::min()); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntZero); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(float1)); + BEAST_EXPECT(result) && BEAST_EXPECT(result->first == 1000000000000000000) && + BEAST_EXPECT(result->second == -normalExp); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == float1); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(floatMinus1)); + BEAST_EXPECT(result) && BEAST_EXPECT(result->first == -1000000000000000000) && + BEAST_EXPECT(result->second == -normalExp); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatMinus1); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(float10)); + BEAST_EXPECT(result) && BEAST_EXPECT(result->first == 1000000000000000000) && + BEAST_EXPECT(result->second == -normalExp + 1); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == float10); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(floatPi)); + BEAST_EXPECT(result) && BEAST_EXPECT(result->first == 3141592653589793000) && + BEAST_EXPECT(result->second == -normalExp); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatPi); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(floatIntMax)); + BEAST_EXPECT(result) && + BEAST_EXPECT(result->first == std::numeric_limits::max()) && + BEAST_EXPECT(result->second == 0); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntMax); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(floatIntMin)); + BEAST_EXPECT(result) && + BEAST_EXPECT(result->first == (std::numeric_limits::min() / 10) - 1) && + BEAST_EXPECT(result->second == 1); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatIntMin); + } + + { + auto const result = hfs.floatToMantissaAndExponent(makeSlice(floatMax)); + BEAST_EXPECT(result) && BEAST_EXPECT(result->first == Number::maxRep) && + BEAST_EXPECT(result->second == Number::maxExponent); + + // roundtrip + auto const result2 = hfs.floatSet(result->first, result->second, 0); + BEAST_EXPECT(result2) && BEAST_EXPECT(*result2 == floatMax); + } + } + + void + testFloatNegate() + { + testcase("floatNegate"); + using namespace test::jtx; + + Env env{*this}; + OpenView ov{*env.current()}; + ApplyContext ac = createApplyContext(env, ov); + auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); + WasmHostFunctionsImpl const hfs(ac, dummyEscrow); + + { + auto const result = hfs.floatNegate(makeSlice(invalid)); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatNegate(makeSlice(floatIntZero)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero); + } + + { + auto const result = hfs.floatNegate(makeSlice(float1)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinus1); + } + + { + auto const result = hfs.floatNegate(makeSlice(floatMinus1)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == float1); + } + + { + auto const result = hfs.floatNegate(makeSlice(floatIntMax)); + auto const expected = hfs.floatFromInt(std::numeric_limits::min() + 1, 0); + BEAST_EXPECT(result && expected) && BEAST_EXPECT(*result == *expected); + } + + { + auto const result = hfs.floatNegate(makeSlice(floatMaxExp)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMinusMaxExp); + } + + { + auto const result = hfs.floatNegate(makeSlice(floatPi)); + auto const negPi = hfs.floatNegate(makeSlice(*result)); + BEAST_EXPECT(result && negPi) && BEAST_EXPECT(*negPi == floatPi); + } + } + + void + testFloatAbs() + { + testcase("floatAbs"); + using namespace test::jtx; + + Env env{*this}; + OpenView ov{*env.current()}; + ApplyContext ac = createApplyContext(env, ov); + auto const dummyEscrow = keylet::escrow(env.master, env.seq(env.master)); + WasmHostFunctionsImpl const hfs(ac, dummyEscrow); + + { + auto const result = hfs.floatAbs(makeSlice(invalid)); + BEAST_EXPECT(!result) && + BEAST_EXPECT(result.error() == HostFunctionError::FLOAT_INPUT_MALFORMED); + } + + { + auto const result = hfs.floatAbs(makeSlice(floatIntZero)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntZero); + } + + { + auto const result = hfs.floatAbs(makeSlice(float1)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == float1); + } + + { + auto const result = hfs.floatAbs(makeSlice(floatMinus1)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == float1); + } + + { + auto const result = hfs.floatAbs(makeSlice(floatIntMax)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatIntMax); + } + + { + auto const result = hfs.floatAbs(makeSlice(floatIntMin)); + auto const negated = hfs.floatNegate(makeSlice(floatIntMin)); + BEAST_EXPECT(result && negated) && BEAST_EXPECT(*result == *negated); + } + + { + auto const result = hfs.floatAbs(makeSlice(floatMinusMaxExp)); + BEAST_EXPECT(result) && BEAST_EXPECT(*result == floatMaxExp); + } + + { + auto const result = hfs.floatAbs(makeSlice(floatMinus3)); + auto const expected = hfs.floatFromInt(3, 0); + BEAST_EXPECT(result && expected) && BEAST_EXPECT(*result == *expected); + } } void testFloats() { + // for checking binary formats manually + // printNumbersBin(); + testTraceFloat(); testFloatFromInt(); testFloatFromUint(); + testFloatFromSTAmount(); + testFloatFromSTNumber(); + testFloatToInt(); + testFloatToMantissaAndExponent(); + testFloatNegate(); + testFloatAbs(); testFloatSet(); testFloatCompare(); testFloatAdd(); diff --git a/src/test/app/TestHostFunctions.h b/src/test/app/TestHostFunctions.h index 7c12329aaa..f1097a7e68 100644 --- a/src/test/app/TestHostFunctions.h +++ b/src/test/app/TestHostFunctions.h @@ -458,6 +458,42 @@ public: return wasm_float::floatFromUintImpl(x, mode); } + Expected + floatFromSTAmount(STAmount const& x, int32_t mode) const override + { + return wasm_float::floatFromSTAmountImpl(x, mode); + } + + Expected + floatFromSTNumber(STNumber const& x, int32_t mode) const override + { + return wasm_float::floatFromSTNumberImpl(x, mode); + } + + Expected + floatToInt(Slice const& x, int32_t mode) const override + { + return wasm_float::floatToIntImpl(x, mode); + } + + virtual Expected + floatToMantissaAndExponent(Slice const& x) const override + { + return wasm_float::floatToMantissaAndExponentImpl(x); + } + + Expected + floatNegate(Slice const& x) const override + { + return wasm_float::floatNegateImpl(x); + } + + Expected + floatAbs(Slice const& x) const override + { + return wasm_float::floatAbsImpl(x); + } + Expected floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override { diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp index b875eaef50..e96ff5b89a 100644 --- a/src/test/app/Wasm_test.cpp +++ b/src/test/app/Wasm_test.cpp @@ -599,7 +599,7 @@ struct Wasm_test : public beast::unit_test::suite TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(floatTestWasm, hfs, 200'000, funcName, {}); - checkResult(re, 1, 110'699); + checkResult(re, 1, 167'965); env.close(); } @@ -608,7 +608,7 @@ struct Wasm_test : public beast::unit_test::suite TestHostFunctions hfs(env, 0); auto re = runEscrowWasm(float0Wasm, hfs, 100'000, funcName, {}); - checkResult(re, 1, 4'259); + checkResult(re, 1, 4'309); env.close(); } } @@ -787,7 +787,7 @@ struct Wasm_test : public beast::unit_test::suite auto re = engine.run(badAlignWasm, hfs, 1'000'000, "test", {}, imports, env.journal); if (BEAST_EXPECTS(re, transToken(re.error()))) { - BEAST_EXPECTS(re->result == 0x684f7941, std::to_string(re->result)); + BEAST_EXPECTS(re->result == 0x47308594, std::to_string(re->result)); } } diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp index 861df4a3ff..a3906e52dd 100644 --- a/src/test/app/wasm_fixtures/fixtures.cpp +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -653,119 +653,267 @@ extern std::string const codecovTestsWasmHex = "2d657874"; extern std::string const floatTestsWasmHex = - "0061736d0100000001430860077f7f7f7f7f7f7f017f60057f7f7f7f7f017f60047f7f7f7f017f60067f7f7f7f7f7f" - "017f60047e7f7f7f017f60057f7e7f7f7f017f60037f7f7e017f6000017f02c9020e08686f73745f6c696205747261" - "6365000108686f73745f6c69620e666c6f61745f66726f6d5f696e74000408686f73745f6c69621274726163655f6f" - "70617175655f666c6f6174000208686f73745f6c69620f666c6f61745f66726f6d5f75696e74000108686f73745f6c" - "696209666c6f61745f736574000508686f73745f6c69620d666c6f61745f636f6d70617265000208686f73745f6c69" - "6209666c6f61745f616464000008686f73745f6c69620e666c6f61745f7375627472616374000008686f73745f6c69" - "620e666c6f61745f6d756c7469706c79000008686f73745f6c69620c666c6f61745f646976696465000008686f7374" - "5f6c696209666c6f61745f706f77000308686f73745f6c69620974726163655f6e756d000608686f73745f6c69620a" - "666c6f61745f726f6f74000308686f73745f6c696209666c6f61745f6c6f6700010302010705030100110619037f01" - "418080c0000b7f0041d28ac0000b7f0041e08ac0000b072e04066d656d6f727902000666696e697368000e0a5f5f64" - "6174615f656e6403010b5f5f686561705f6261736503020af20e01ef0e01047f230041206b22012400418080c00041" - "1d41014100410010001a200142003703100240428ce000200141106a22004108410010014108460440419d80c00041" - "172000410810021a41b480c000411e20004108410110001a0c010b41d280c000411e41014100410010001a0b200142" - "8ce0003703180240200141186a4108200141106a2200410841001003410846044041f080c00041172000410810021a" - "0c010b418781c000411e41014100410010001a0b0240410242fb00200141106a2200410841001004410846044041a5" - "81c00041212000410810021a0c010b41c681c000412641014100410010001a0b41ec81c0004115418182c000410810" - "021a419182c0004116418982c000410810021a41a782c000411b41014100410010001a200142003703180240420120" - "0141186a2200410841001001410846044041c282c000410f2000410810021a0c010b41d182c0004116410141004100" - "10001a0b0240200141186a4108418182c0004108100545044041e782c000411b41014100410010001a0c010b418283" - "c000411b41014100410010001a0b0240200141186a4108418982c000410810054101460440419d83c0004123410141" - "00410010001a0c010b41c083c000412441014100410010001a0b0240418982c0004108200141186a41081005410246" - "044041e483c000412341014100410010001a0c010b418784c000412441014100410010001a0b41ab84c00041204101" - "4100410010001a200142d487b6f4c7d4b1c000370310410921000340200141106a22024108418182c0004108200241" - "08410010061a200041016b22000d000b20014200370318420a200141186a22004108410010011a0240200041082002" - "4108100545044041cb84c000411441014100410010001a0c010b41df84c000411341014100410010001a0b410b2100" - "0340200141106a22024108418182c000410820024108410010071a200041016b22000d000b024020024108418982c0" - "004108100545044041f284c000411941014100410010001a0c010b418b85c000411841014100410010001a0b41a385" - "c000412341014100410010001a20014200370300420a20014108410010011a200142d487b6f4c7d4b1c00037030841" - "0621000340200141086a220241082001410820024108410010081a200041016b22000d000b2001420037031042c084" - "3d200141106a22004108410010011a02402000410820024108100545044041c685c000411941014100410010001a0c" - "010b41df85c000411841014100410010001a0b410721000340200141086a220241082001410820024108410010091a" - "200041016b22000d000b20014200370318417f4201200141186a22004108410010041a024020024108200041081005" - "45044041f785c000411741014100410010001a0c010b418e86c000411641014100410010001a0b41a486c000411741" - "014100410010001a20014200370308418182c00041084103200141086a220041084100100a1a41bb86c00041122000" - "410810021a418982c00041084106200041084100100a1a41cd86c00041182000410810021a20014200370310420920" - "0141106a22024108410010011a200241084102200041084100100a1a41e586c00041142000410810021a2002410841" - "00200041084100100a1a41f986c00041172000410810021a200142003703184200200141186a22034108410010011a" - "200341084102200041084100100a1a419087c00041142000410810021a41a487c00041382003410841002000410841" - "00100aac100b1a41dc87c000411841014100410010001a20014200370308420920004108410010011a200142003703" - "10200041084102200241084100100c1a41f487c00041122002410810021a200041084103200241084100100c1a4186" - "88c00041122002410810021a2001420037031842c0843d20034108410010011a200341084103200241084100100c1a" - "419888c00041182002410810021a200341084106200241084100100c1a41b088c000411c2002410810021a41cc88c0" - "00411741014100410010001a2001420037031042c0843d20024108410010011a200142003703182002410820034108" - "4100100d1a41e388c00041142003410810021a41f788c000411a41014100410010001a20014200370318418182c000" - "4108418982c000410820034108410010081a0240418982c0004108200341081005450440419189c000411641014100" - "410010001a0c010b41a789c000411541014100410010001a0b418982c0004108418982c0004108200141186a220041" - "08410010081a0240418182c000410820004108100545044041bc89c000411741014100410010001a0c010b41d389c0" - "00411641014100410010001a0b41e989c000411a41014100410010001a2001420037031020014200370318420a2001" - "41186a22024108410010011a418182c000410820024108200141106a22004108410010091a41838ac0004119200041" - "0810021a418182c00041082000410820004108410010091a419c8ac000410f2000410810021a024020024108200041" - "08100545044041ab8ac000411441014100410010001a0c010b41bf8ac000411341014100410010001a0b200141206a" - "240041010b0bdc0a0100418080c0000bd20a0a24242420746573745f666c6f61745f66726f6d5f7761736d20242424" - "2020666c6f61742066726f6d206936342031323330303a2020666c6f61742066726f6d206936342031323330302061" - "73204845583a2020666c6f61742066726f6d206936342031323330303a206661696c65642020666c6f61742066726f" - "6d207536342031323330303a2020666c6f61742066726f6d207536342031323330303a206661696c65642020666c6f" - "61742066726f6d2065787020322c206d616e7469737361203132333a2020666c6f61742066726f6d2065787020322c" - "206d616e746973736120333a206661696c65642020666c6f61742066726f6d20636f6e737420313ad4838d7ea4c680" - "0094838d7ea4c680002020666c6f61742066726f6d20636f6e7374202d313a0a24242420746573745f666c6f61745f" - "636f6d70617265202424242020666c6f61742066726f6d20313a2020666c6f61742066726f6d20313a206661696c65" - "642020666c6f61742066726f6d2031203d3d20464c4f41545f4f4e452020666c6f61742066726f6d203120213d2046" - "4c4f41545f4f4e452020666c6f61742066726f6d2031203e20464c4f41545f4e454741544956455f4f4e452020666c" - "6f61742066726f6d203120213e20464c4f41545f4e454741544956455f4f4e452020464c4f41545f4e454741544956" - "455f4f4e45203c20666c6f61742066726f6d20312020464c4f41545f4e454741544956455f4f4e4520213c20666c6f" - "61742066726f6d20310a24242420746573745f666c6f61745f6164645f737562747261637420242424202072657065" - "61746564206164643a20676f6f6420207265706561746564206164643a206261642020726570656174656420737562" - "74726163743a20676f6f64202072657065617465642073756274726163743a206261640a24242420746573745f666c" - "6f61745f6d756c7469706c795f6469766964652024242420207265706561746564206d756c7469706c793a20676f6f" - "6420207265706561746564206d756c7469706c793a2062616420207265706561746564206469766964653a20676f6f" - "6420207265706561746564206469766964653a206261640a24242420746573745f666c6f61745f706f772024242420" - "20666c6f61742063756265206f6620313a2020666c6f61742036746820706f776572206f66202d313a2020666c6f61" - "7420737175617265206f6620393a2020666c6f61742030746820706f776572206f6620393a2020666c6f6174207371" - "75617265206f6620303a2020666c6f61742030746820706f776572206f6620302028657870656374696e6720494e56" - "414c49445f504152414d53206572726f72293a0a24242420746573745f666c6f61745f726f6f74202424242020666c" - "6f61742073717274206f6620393a2020666c6f61742063627274206f6620393a2020666c6f61742063627274206f66" - "20313030303030303a2020666c6f61742036746820726f6f74206f6620313030303030303a0a24242420746573745f" - "666c6f61745f6c6f672024242420206c6f675f3130206f6620313030303030303a0a24242420746573745f666c6f61" - "745f6e65676174652024242420206e656761746520636f6e737420313a20676f6f6420206e656761746520636f6e73" - "7420313a2062616420206e656761746520636f6e7374202d313a20676f6f6420206e656761746520636f6e7374202d" - "313a206261640a24242420746573745f666c6f61745f696e76657274202424242020696e76657274206120666c6f61" - "742066726f6d2031303a2020696e7665727420616761696e3a2020696e766572742074776963653a20676f6f642020" - "696e766572742074776963653a20626164004d0970726f64756365727302086c616e6775616765010452757374000c" - "70726f6365737365642d6279010572757374631d312e38372e30202831373036376539616320323032352d30352d30" - "3929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d6578" - "74"; + "0061736d01000000014f0a60057f7f7f7f7f017f60077f7f7f7f7f7f7f017f60047f7f7f7f017f60067f7f7f7f7f7f" + "017f60047e7f7f7f017f60057f7e7f7f7f017f60037f7f7e017f60037f7f7f006000017f60027f7f017f02ba031308" + "686f73745f6c6962057472616365000008686f73745f6c69620e666c6f61745f66726f6d5f696e74000408686f7374" + "5f6c69620f666c6f61745f66726f6d5f75696e74000008686f73745f6c696209666c6f61745f736574000508686f73" + "745f6c69620d666c6f61745f636f6d70617265000208686f73745f6c696209666c6f61745f616464000108686f7374" + "5f6c69620e666c6f61745f7375627472616374000108686f73745f6c69620e666c6f61745f6d756c7469706c790001" + "08686f73745f6c69620c666c6f61745f646976696465000108686f73745f6c696209666c6f61745f706f7700030868" + "6f73745f6c69620974726163655f6e756d000608686f73745f6c69620a666c6f61745f726f6f74000308686f73745f" + "6c696209666c6f61745f6c6f67000003656e760c666c6f61745f746f5f696e74000003656e761e666c6f61745f746f" + "5f6d616e74697373615f616e645f6578706f6e656e74000303656e760c666c6f61745f6e6567617465000203656e76" + "09666c6f61745f616273000203656e7613666c6f61745f66726f6d5f7374616d6f756e74000003656e7613666c6f61" + "745f66726f6d5f73746e756d626572000003040307080905030100110619037f01418080c0000b7f0041a0a1c0000b" + "7f0041a0a1c0000b072e04066d656d6f727902000666696e69736800140a5f5f646174615f656e6403010b5f5f6865" + "61705f6261736503020ae52c031f002000200141014100410010001a418080c00041022002410c410110001a0bff2b" + "020f7f017e23004180016b22002400418280c000411d41014100410010001a200041f8006a41003602002000420037" + "03700240428ce000200041f0006a2202410c410010012201410c460440419f80c00041172002101341b680c000411e" + "2002410c410110001a0c010b41d480c000411e41014100410010001a0b2000428ce0003703600240200041e0006a41" + "08200041f0006a2202410c41001002410c4604402001410c46210841f280c0004117200210130c010b418981c00041" + "1e41014100410010001a0b0240410242fb00200041f0006a2201410c41001003410c46044041a781c0004121200110" + "130c010b41c881c000412641014100410010001a410021080b41ee81c0004115418382c0001013418f82c000411641" + "a582c000101341b182c000411b41014100410010001a200041f8006a41003602002000420037037002404201200041" + "f0006a2202410c410010012201410c46044041cc82c000410f200210130c010b41db82c00041164101410041001000" + "1a0b027f200041f0006a410c418382c000410c100445044041f182c000411b41014100410010001a2001410c460c01" + "0b418c83c000412341014100410010001a41000b21090240200041f0006a410c41a582c000410c1004410146044041" + "af83c000412341014100410010001a0c010b4100210941d283c000412c41014100410010001a0b024041a582c00041" + "0c200041f0006a410c1004410246044041fe83c000412341014100410010001a0c010b4100210941a184c000412c41" + "014100410010001a0b41cd84c000412041014100410010001a200041e8006a418b82c0002800003602002000418382" + "c000290000370360410921040340200041e0006a2201410c418382c000410c2001410c410010051a200441016b2204" + "0d000b200041f8006a410036020020004200370370420a200041f0006a410c41001001410c46220a45044041ed84c0" + "00411741014100410010001a0b0240200041f0006a410c200041e0006a410c1004450440418485c000411441014100" + "410010001a0c010b4100210a419885c000411641014100410010001a0b410b21040340200041e0006a2201410c4183" + "82c000410c2001410c410010061a200441016b22040d000b02402001410c41a582c000410c100445044041ae85c000" + "411941014100410010001a0c010b4100210a41c785c000411b41014100410010001a0b41e285c00041234101410041" + "0010001a200041c8006a410036020020004200370340420a200041406b410c410010011a200041d8006a418b82c000" + "2800003602002000418382c000290000370350410621040340200041d0006a2201410c200041406b410c2001410c41" + "0010071a200441016b22040d000b200041e8006a41003602002000420037036042c0843d200041e0006a2202410c41" + "0010011a02402002410c2001410c10042201450440418586c000411941014100410010001a0c010b419e86c000411b" + "41014100410010001a0b200145210c410721040340200041d0006a2201410c200041406b410c2001410c410010081a" + "200441016b22040d000b200041f8006a410036020020004200370370417f4201200041f0006a2202410c410010031a" + "02402001410c2002410c100445044041b986c000411741014100410010001a0c010b41d086c0004119410141004100" + "10001a4100210c0b41e986c000411741014100410010001a200041d8006a2202410036020020004200370350418382" + "c000410c4103200041d0006a2204410c410010091a418087c00041122004101341a582c000410c41062004410c4100" + "10091a419287c000411820041013200041e8006a22064100360200200042003703604209200041e0006a2203410c41" + "0010011a2003410c41022004410c410010091a41aa87c0004114200410132003410c41002004410c410010091a41be" + "87c000411720041013200041f8006a22014100360200200042003703704200200041f0006a2205410c410010011a20" + "05410c41022004410c410010091a41d587c00041142004101341e987c00041382005410c41002004410c41001009ac" + "100a1a41a188c000411841014100410010001a200241003602002000420037035042092004410c410010011a200641" + "00360200200042003703602004410c41022003410c4100100b1a41b988c0004112200310132004410c41032003410c" + "4100100b1a41cb88c000411220031013200141003602002000420037037042c0843d2005410c410010011a2005410c" + "41032003410c4100100b1a41dd88c0004118200310132005410c41062003410c4100100b1a41f588c000411c200310" + "13419189c000411741014100410010001a200641003602002000420037036042c0843d2003410c410010011a200141" + "00360200200042003703702003410c2005410c4100100c1a41a889c00041142005101341bc89c000411a4101410041" + "0010001a2001410036020020004200370370418382c000410c41a582c000410c2005410c410010071a024041a582c0" + "00410c2005410c1004220145044041d689c000411641014100410010001a0c010b41ec89c000411841014100410010" + "001a0b41a582c000410c41a582c000410c200041f0006a2202410c410010071a0240418382c000410c2002410c1004" + "450440200145210d41848ac000411741014100410010001a0c010b419b8ac000411941014100410010001a0b41b48a" + "c000411a41014100410010001a200041e8006a410036020020004200370360200041f8006a41003602002000420037" + "0370420a200041f0006a2202410c410010011a418382c000410c2002410c200041e0006a2201410c410010081a41ce" + "8ac000411920011013418382c000410c2001410c2001410c410010081a41e78ac000410f2001101302402002410c20" + "01410c1004220e45044041f68ac000411441014100410010001a0c010b418a8bc000411641014100410010001a0b41" + "00210141a08bc000411a41014100410010001a200042003703400240418382c000410c200041406b41084100100d22" + "0641084604402000290340220f42015104404101210141ba8bc000411741014100410010001a0c020b41d18bc00041" + "1941014100410010001a41ea8bc0004108200f100a1a0c010b41f28bc000412441014100410010001a41968cc00041" + "0f2006ac100a1a0b41002104024041a582c000410c200041406b41084100100d220641084604402000290340220f42" + "7f51044041a58cc000411841014100410010001a200121040c020b41bd8cc000411a41014100410010001a41ea8bc0" + "004108200f100a1a0c010b41d78cc000412541014100410010001a41968cc000410f2006ac100a1a0b410021012000" + "41d8006a41003602002000420037035042ffffffffffffffffff00200041d0006a2202410c410010011a0240200241" + "0c200041406b41084100100d220641084604402000290340220f42ffffffffffffffffff0051044041fc8cc000411e" + "41014100410010001a200421010c020b419a8dc000412041014100410010001a41ba8dc000410d42ffffffffffffff" + "ffff00100a1a41ea8bc0004108200f100a1a0c010b41c78dc000412b41014100410010001a41968cc000410f2006ac" + "100a1a0b41002106200041e8006a4100360200200042003703604200200041e0006a2202410c410010011a02402002" + "410c200041406b41084100100d220241084604402000290340220f50044041f28dc000411741014100410010001a20" + "0121060c020b41898ec000411941014100410010001a41ea8bc0004108200f100a1a0c010b41a28ec0004124410141" + "00410010001a41968cc000410f2002ac100a1a0b41002104200041f8006a410036020020004200370370417f420120" + "0041f0006a2201410c410010031a02402001410c200041406b41084100100d220141084604402000290340220f5004" + "4041c68ec000412541014100410010001a200621040c020b41eb8ec000412741014100410010001a41ea8bc0004108" + "200f100a1a0c010b41928fc000413241014100410010001a41968cc000410f2001ac100a1a0b0240200041f0006a41" + "0c200041406b41084101100d220141084604402000290340220f50044041c48fc000412741014100410010001a0c02" + "0b4100210441eb8fc000412941014100410010001a41ea8bc0004108200f100a1a0c010b41002104419490c0004134" + "41014100410010001a41968cc000410f2001ac100a1a0b4100210141c890c000412c41014100410010001a20004200" + "370320200041003602040240418382c000410c200041206a4108200041046a4104100e2206410c4604402000280204" + "2206416e462000290320220f42808090bbbad6adf00d5171450440419d91c000412b41014100410010001a41c891c0" + "00412f200f100a1a41f791c000411f2006ac100a1a0c020b4101210141f490c000412941014100410010001a0c010b" + "419692c000413641014100410010001a41968cc000410f2006ac100a1a0b2000420037033041002106200041003602" + "08024041a582c000410c200041306a4108200041086a4104100e2202410c46044020002802082202416e4620002903" + "30220f428080f0c4c5a9d28f72517145044041f692c000412c41014100410010001a41a293c0004130200f100a1a41" + "f791c000411f2002ac100a1a0c020b41cc92c000412a41014100410010001a200121060c010b41d293c00041374101" + "4100410010001a41968cc000410f2002ac100a1a0b41002101200041e8006a410036020020004200370360420a2000" + "41e0006a2202410c410010011a200042003703402000410036020c02402002410c200041406b41082000410c6a4104" + "100e2202410c460440200028020c2202416f462000290340220f42808090bbbad6adf00d517145044041b394c00041" + "2c41014100410010001a41c891c000412f200f100a1a41df94c000411f2002ac100a1a0c020b418994c000412a4101" + "4100410010001a200621010c010b41fe94c000413741014100410010001a41968cc000410f2002ac100a1a0b410021" + "06200041f8006a4100360200200042003703704200200041f0006a2202410c410010011a2000420037035020004100" + "36021002402002410c200041d0006a4108200041106a4104100e2202410c4604402000290350220f50200028021022" + "02418080808078467145044041de95c000412b41014100410010001a418996c000411d200f100a1a41a696c0004127" + "2002ac100a1a0c020b41b595c000412941014100410010001a200121060c010b41cd96c00041364101410041001000" + "1a41968cc000410f2002ac100a1a0b4100210141bc89c000411a41014100410010001a200041c8006a410036020020" + "0042003703400240418382c000410c200041406b2203410c100f2202410c460440200341a582c00010150440418397" + "c000412b41014100410010001a0c020b4101210141ae97c000411741014100410010001a0c010b41c597c000412441" + "014100410010001a41968cc000410f2002ac100a1a0b41002102200041d8006a410036020020004200370350024041" + "a582c000410c200041d0006a2205410c100f2203410c4604402005418382c0001015044041e997c000412c41014100" + "410010001a0c020b419598c000411841014100410010001a200121020c010b41ad98c000412541014100410010001a" + "41968cc000410f2003ac100a1a0b200041e8006a4100360200200042003703604200200041e0006a2203410c410010" + "011a200041f8006a41003602002000420037037002402003410c200041f0006a2205410c100f2201410c4604402005" + "20031015044041d298c000412b41014100410010001a0c020b41fd98c000411741014100410010001a2002210b0c01" + "0b419499c000412441014100410010001a41968cc000410f2001ac100a1a0b4100210141b899c00041174101410041" + "0010001a200041186a4100360200200042003703100240418382c000410c200041106a2203410c10102202410c4604" + "402003418382c0001015044041cf99c000412841014100410010001a0c020b4101210141f799c00041144101410041" + "0010001a0c010b418b9ac000412141014100410010001a41968cc000410f2002ac100a1a0b41002102200041286a41" + "0036020020004200370320024041a582c000410c200041206a2205410c10102203410c4604402005418382c0001015" + "044041ac9ac000412941014100410010001a0c020b41d59ac000411541014100410010001a200121020c010b41ea9a" + "c000412241014100410010001a41968cc000410f2003ac100a1a0b41002101200041386a4100360200200042003703" + "304200200041306a2205410c410010011a200041c8006a41003602002000420037034002402005410c200041406b22" + "07410c10102203410c4604402007200510150440418c9bc000412841014100410010001a0c020b41b49bc000411441" + "014100410010001a200221010c010b41c89bc000412141014100410010001a41968cc000410f2003ac100a1a0b4100" + "2102200041d8006a4100360200200042003703504276200041d0006a2203410c410010011a200041e8006a41003602" + "0020004200370360420a200041e0006a2205410c410010011a200041f8006a41003602002000420037037002402003" + "410c200041f0006a2207410c10102203410c460440200720051015044041e99bc000412a41014100410010001a0c02" + "0b41939cc000411641014100410010001a200121020c010b41a99cc000412341014100410010001a41968cc000410f" + "2003ac100a1a0b4100210141cc9cc000412141014100410010001a200042c0808080d0a0fdf000370050200041f800" + "6a41003602002000420037037002400240200041d0006a4108200041f0006a2205410c410010112203410c46044041" + "ed9cc000412220051013200042003703602005410c200041e0006a41084100100d22034108470d012000290360220f" + "4280c2d72f51044041012101418f9dc000411d41014100410010001a0c030b41ac9dc000411f41014100410010001a" + "41cb9dc000411c200f100a1a0c020b419b9ec000411f41014100410010001a41ba9ec00041102003ac100a1a0c010b" + "41e79dc000413441014100410010001a41968cc000410f2003ac100a1a0b4100210341ca9ec0004121410141004100" + "10001a200041ffffff8f7f36006820004281eceedce494ccf9c000370060200041f8006a4100360200200042003703" + "7002400240200041e0006a410c200041f0006a2207410c410010122205410c46044041eb9ec000411c200710132000" + "42003703502007410c200041d0006a41084100100d22054108470d012000290350220f42fb00510440410121034187" + "9fc000411b41014100410010001a0c030b41a29fc000411d41014100410010001a41bf9fc0004116200f100a1a0c02" + "0b4187a0c000411d41014100410010001a41ba9ec00041102005ac100a1a0c010b41d59fc000413241014100410010" + "001a41968cc000410f2005ac100a1a0b410021050240418382c000410c200041f0006a2207410c41001012410c4604" + "4041a4a0c000411a200710132007410c418382c000410c100445044041bea0c000412041014100410010001a200321" + "050c020b41dea0c000412241014100410010001a0c010b4180a1c000412041014100410010001a0b20004180016a24" + "00200e452008200971200a71200c71200d71200471200671200b71200271200171200571710b4201047f410c210202" + "40034020002d0000220320012d00002204460440200041016a2100200141016a2101200241016b22020d010c020b0b" + "200320046b21050b20050b0baa210100418080c0000ba02120200a24242420746573745f666c6f61745f66726f6d5f" + "7761736d202424242020666c6f61742066726f6d206936342031323330303a2020666c6f61742066726f6d20693634" + "203132333030206173204845583a2020666c6f61742066726f6d206936342031323330303a206661696c6564202066" + "6c6f61742066726f6d207536342031323330303a2020666c6f61742066726f6d207536342031323330303a20666169" + "6c65642020666c6f61742066726f6d2065787020322c206d616e7469737361203132333a2020666c6f61742066726f" + "6d2065787020322c206d616e746973736120333a206661696c65642020666c6f61742066726f6d20636f6e73742031" + "3a0de0b6b3a7640000ffffffee2020666c6f61742066726f6d20636f6e7374202d313af21f494c589c0000ffffffee" + "0a24242420746573745f666c6f61745f636f6d70617265202424242020666c6f61742066726f6d20313a2020666c6f" + "61742066726f6d20313a206661696c65642020666c6f61742066726f6d2031203d3d20464c4f41545f4f4e45202066" + "6c6f61742066726f6d203120213d20464c4f41545f4f4e452c206661696c65642020666c6f61742066726f6d203120" + "3e20464c4f41545f4e454741544956455f4f4e452020666c6f61742066726f6d203120213e20464c4f41545f4e4547" + "41544956455f4f4e452c206661696c65642020464c4f41545f4e454741544956455f4f4e45203c20666c6f61742066" + "726f6d20312020464c4f41545f4e454741544956455f4f4e4520213c20666c6f61742066726f6d20312c206661696c" + "65640a24242420746573745f666c6f61745f6164645f7375627472616374202424242020666c6f61742066726f6d20" + "31303a206661696c656420207265706561746564206164643a20676f6f6420207265706561746564206164643a2066" + "61696c6564202072657065617465642073756274726163743a20676f6f642020726570656174656420737562747261" + "63743a206661696c65640a24242420746573745f666c6f61745f6d756c7469706c795f646976696465202424242020" + "7265706561746564206d756c7469706c793a20676f6f6420207265706561746564206d756c7469706c793a20666169" + "6c656420207265706561746564206469766964653a20676f6f6420207265706561746564206469766964653a206661" + "696c65640a24242420746573745f666c6f61745f706f77202424242020666c6f61742063756265206f6620313a2020" + "666c6f61742036746820706f776572206f66202d313a2020666c6f617420737175617265206f6620393a2020666c6f" + "61742030746820706f776572206f6620393a2020666c6f617420737175617265206f6620303a2020666c6f61742030" + "746820706f776572206f6620302028657870656374696e6720494e56414c49445f504152414d53206572726f72293a" + "0a24242420746573745f666c6f61745f726f6f74202424242020666c6f61742073717274206f6620393a2020666c6f" + "61742063627274206f6620393a2020666c6f61742063627274206f6620313030303030303a2020666c6f6174203674" + "6820726f6f74206f6620313030303030303a0a24242420746573745f666c6f61745f6c6f672024242420206c6f675f" + "3130206f6620313030303030303a0a24242420746573745f666c6f61745f6e65676174652024242420206e65676174" + "6520636f6e737420313a20676f6f6420206e656761746520636f6e737420313a206661696c656420206e6567617465" + "20636f6e7374202d313a20676f6f6420206e656761746520636f6e7374202d313a206661696c65640a242424207465" + "73745f666c6f61745f696e76657274202424242020696e76657274206120666c6f61742066726f6d2031303a202069" + "6e7665727420616761696e3a2020696e766572742074776963653a20676f6f642020696e766572742074776963653a" + "206661696c65640a24242420746573745f666c6f61745f746f5f696e74202424242020666c6f61745f746f5f696e74" + "2831293a20676f6f642020666c6f61745f746f5f696e742831293a206661696c656420202020676f743a2020666c6f" + "61745f746f5f696e742831293a206661696c65642077697468206572726f72202020206572726f7220636f64653a20" + "20666c6f61745f746f5f696e74282d31293a20676f6f642020666c6f61745f746f5f696e74282d31293a206661696c" + "65642020666c6f61745f746f5f696e74282d31293a206661696c65642077697468206572726f722020666c6f61745f" + "746f5f696e74286936343a3a4d4158293a20676f6f642020666c6f61745f746f5f696e74286936343a3a4d4158293a" + "206661696c65642020202065787065637465643a2020666c6f61745f746f5f696e74286936343a3a4d4158293a2066" + "61696c65642077697468206572726f722020666c6f61745f746f5f696e742830293a20676f6f642020666c6f61745f" + "746f5f696e742830293a206661696c65642020666c6f61745f746f5f696e742830293a206661696c65642077697468" + "206572726f722020666c6f61745f746f5f696e7428302e312c20746f5f6e656172657374293a20676f6f642020666c" + "6f61745f746f5f696e7428302e312c20746f5f6e656172657374293a206661696c65642020666c6f61745f746f5f69" + "6e7428302e312c20746f5f6e656172657374293a206661696c65642077697468206572726f722020666c6f61745f74" + "6f5f696e7428302e312c20746f77617264735f7a65726f293a20676f6f642020666c6f61745f746f5f696e7428302e" + "312c20746f77617264735f7a65726f293a206661696c65642020666c6f61745f746f5f696e7428302e312c20746f77" + "617264735f7a65726f293a206661696c65642077697468206572726f720a24242420746573745f666c6f61745f746f" + "5f6d616e74697373615f616e645f6578706f6e656e74202424242020666c6f61745f746f5f6d616e74697373615f61" + "6e645f6578706f6e656e742831293a20676f6f642020666c6f61745f746f5f6d616e74697373615f616e645f657870" + "6f6e656e742831293a206661696c6564202020206578706563746564206d616e746973736120313030303030303030" + "303030303030303030302c20676f743a202020206578706563746564206578706f6e656e74202d31382c20676f743a" + "2020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e742831293a206661696c656420776974" + "68206572726f722020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e74282d31293a20676f" + "6f642020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e74282d31293a206661696c656420" + "2020206578706563746564206d616e7469737361202d313030303030303030303030303030303030302c20676f743a" + "2020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e74282d31293a206661696c6564207769" + "7468206572726f722020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e74283130293a2067" + "6f6f642020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e74283130293a206661696c6564" + "202020206578706563746564206578706f6e656e74202d31372c20676f743a2020666c6f61745f746f5f6d616e7469" + "7373615f616e645f6578706f6e656e74283130293a206661696c65642077697468206572726f722020666c6f61745f" + "746f5f6d616e74697373615f616e645f6578706f6e656e742830293a20676f6f642020666c6f61745f746f5f6d616e" + "74697373615f616e645f6578706f6e656e742830293a206661696c6564202020206578706563746564206d616e7469" + "73736120302c20676f743a202020206578706563746564206578706f6e656e74202d323134373438333634382c2067" + "6f743a2020666c6f61745f746f5f6d616e74697373615f616e645f6578706f6e656e742830293a206661696c656420" + "77697468206572726f722020666c6f61745f6e65676174652831293a206661696c6564202d20726573756c74206d69" + "736d617463682020666c6f61745f6e65676174652831293a20676f6f642020666c6f61745f6e65676174652831293a" + "206661696c65642077697468206572726f722020666c6f61745f6e6567617465282d31293a206661696c6564202d20" + "726573756c74206d69736d617463682020666c6f61745f6e6567617465282d31293a20676f6f642020666c6f61745f" + "6e6567617465282d31293a206661696c65642077697468206572726f722020666c6f61745f6e65676174652830293a" + "206661696c6564202d20726573756c74206d69736d617463682020666c6f61745f6e65676174652830293a20676f6f" + "642020666c6f61745f6e65676174652830293a206661696c65642077697468206572726f720a24242420746573745f" + "666c6f61745f616273202424242020666c6f61745f6162732831293a206661696c6564202d20726573756c74206d69" + "736d617463682020666c6f61745f6162732831293a20676f6f642020666c6f61745f6162732831293a206661696c65" + "642077697468206572726f722020666c6f61745f616273282d31293a206661696c6564202d20726573756c74206d69" + "736d617463682020666c6f61745f616273282d31293a20676f6f642020666c6f61745f616273282d31293a20666169" + "6c65642077697468206572726f722020666c6f61745f6162732830293a206661696c6564202d20726573756c74206d" + "69736d617463682020666c6f61745f6162732830293a20676f6f642020666c6f61745f6162732830293a206661696c" + "65642077697468206572726f722020666c6f61745f616273282d3130293a206661696c6564202d20726573756c7420" + "6d69736d617463682020666c6f61745f616273282d3130293a20676f6f642020666c6f61745f616273282d3130293a" + "206661696c65642077697468206572726f720a24242420746573745f666c6f61745f66726f6d5f7374616d6f756e74" + "202424242020666c6f61742066726f6d2058525020616d6f756e74202831303020585250293a202058525020616d6f" + "756e7420636f6e76657273696f6e3a20676f6f64202058525020616d6f756e7420636f6e76657273696f6e3a206661" + "696c6564202020206578706563746564203130303030303030302c20676f743a202058525020616d6f756e7420636f" + "6e76657273696f6e3a206661696c6564202d20666c6f61745f746f5f696e74206572726f722020666c6f6174206672" + "6f6d2058525020616d6f756e743a206661696c656420202020726573756c745f73697a653a0a24242420746573745f" + "666c6f61745f66726f6d5f73746e756d626572202424242020666c6f61742066726f6d2053544e756d626572202831" + "3233293a202053544e756d62657220636f6e76657273696f6e3a20676f6f64202053544e756d62657220636f6e7665" + "7273696f6e3a206661696c6564202020206578706563746564203132332c20676f743a202053544e756d6265722063" + "6f6e76657273696f6e3a206661696c6564202d20666c6f61745f746f5f696e74206572726f722020666c6f61742066" + "726f6d2053544e756d6265723a206661696c65642020666c6f61742066726f6d2053544e756d626572202831293a20" + "2053544e756d626572283129203d3d20464c4f41545f4f4e453a20676f6f64202053544e756d626572283129203d3d" + "20464c4f41545f4f4e453a206661696c65642020666c6f61742066726f6d2053544e756d6265722831293a20666169" + "6c6564004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d62790105" + "72757374631d312e38392e30202832393438333838336520323032352d30382d303429002c0f7461726765745f6665" + "617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d657874"; extern std::string const float0Hex = "0061736d0100000001290560057f7f7f7f7f017f60047e7f7f7f017f60077f7f7f7f7f7f7f017f60047f7f7f7f017f" "6000017f025f0408686f73745f6c6962057472616365000008686f73745f6c69620e666c6f61745f66726f6d5f696e" "74000108686f73745f6c69620e666c6f61745f7375627472616374000208686f73745f6c69620d666c6f61745f636f" - "6d7061726500030302010405030100110619037f01418080c0000b7f00419281c0000b7f0041a081c0000b072e0406" - "6d656d6f727902000666696e69736800040a5f5f646174615f656e6403010b5f5f686561705f6261736503020abe02" - "01bb0201017f23808080800041206b2200248080808000418080c0800041154100410041001080808080001a200042" - "003703084200200041086a410841001081808080001a20004200370310420a200041106a410841001081808080001a" - "200042003703180240200041106a4108200041106a4108200041186a410841001082808080004108460d00419580c0" - "800041154100410041001080808080001a0b02400240200041086a4108200041186a41081083808080000d0041aa80" - "c0800041174100410041001080808080001a0c010b41c180c0800041164100410041001080808080001a0b02400240" - "200041086a410841d780c0800041081083808080000d0041df80c08000411a4100410041001080808080001a0c010b" - "41f980c0800041194100410041001080808080001a0b200041206a24808080800041010b0b9c010100418080c0000b" - "92010a24242420746573745f666c6f61745f30202424242020666c6f61742031302d31303a206661696c6564202066" - "6c6f6174203020636f6d706172653a20676f6f642020666c6f6174203020636f6d706172653a206261648000000000" - "0000002020464c4f41545f5a45524f20636f6d706172653a20676f6f642020464c4f41545f5a45524f20636f6d7061" - "72653a20626164009502046e616d65001110666c6f61745f74657374732e7761736d01da0105002b5f5a4e38787270" - "6c5f73746434686f7374357472616365313768616338383262323664656162656436364501355f5a4e387872706c5f" - "73746434686f73743134666c6f61745f66726f6d5f696e74313768303234306638653361383964313939654502355f" - "5a4e387872706c5f73746434686f73743134666c6f61745f7375627472616374313768643634306331353233343534" - "323935634503345f5a4e387872706c5f73746434686f73743133666c6f61745f636f6d706172653137683663386465" - "656231323864393638386645040666696e697368071201000f5f5f737461636b5f706f696e746572090a0100072e72" - "6f64617461004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d6279" - "010572757374631d312e38392e30202832393438333838336520323032352d30382d3034290094010f746172676574" - "5f6665617475726573082b0b62756c6b2d6d656d6f72792b0f62756c6b2d6d656d6f72792d6f70742b1663616c6c2d" - "696e6469726563742d6f7665726c6f6e672b0a6d756c746976616c75652b0f6d757461626c652d676c6f62616c732" - "b136e6f6e7472617070696e672d6670746f696e742b0f7265666572656e63652d74797065732b087369676e2d65787" - "4"; + "6d7061726500030302010405030100110619037f01418080c0000b7f00419681c0000b7f0041a081c0000b072e0406" + "6d656d6f727902000666696e69736800040a5f5f646174615f656e6403010b5f5f686561705f6261736503020ad302" + "01d00201017f23808080800041206b2200248080808000418080c0800041154101410041001080808080001a200041" + "086a410036020020004200370300200041106a41086a410036020020004200370310024002400240420a2000410c41" + "00108180808000410c470d002000410c2000410c200041106a410c4100108280808000410c470d0102400240200041" + "106a410c200041106a410c1083808080000d00419580c0800041174101410041001080808080001a0c010b41ac80c0" + "800041164101410041001080808080001a0b0240200041106a410c41c280c08000410c1083808080000d0041ce80c0" + "8000411a4101410041001080808080001a0c030b41e880c0800041194101410041001080808080001a0c020b418181" + "c0800041154101410041001080808080001a0c010b418181c0800041154101410041001080808080001a0b20004120" + "6a24808080800041010b0ba0010100418080c0000b96010a24242420746573745f666c6f61745f3020242424202066" + "6c6f6174203020636f6d706172653a20676f6f642020666c6f6174203020636f6d706172653a206261640000000000" + "000000800000002020464c4f41545f5a45524f20636f6d706172653a20676f6f642020464c4f41745f5a45524f2063" + "6f6d706172653a206261642020666c6f61742031302d31303a206661696c6564009503046e616d65000d0c666c6f61" + "745f302e7761736d01de0205004c5f5a4e31367872706c5f7761736d5f7374646c696234686f73743232686f73745f" + "646566696e65645f66756e6374696f6e73357472616365313768653738323066313637383330383338364501565f5a" + "4e31367872706c5f7761736d5f7374646c696234686f73743232686f73745f646566696e65645f66756e6374696f6e" + "733134666c6f61745f66726f6d5f696e74313768646463636262643266613366663431634502565f5a4e3136787270" + "6c5f7761736d5f7374646c696234686f73743232686f73745f646566696e65645f66756e6374696f6e733134666c6f" + "61745f7375627472616374313768313765643838343131303333663437624503555f5a4e31367872706c5f7761736d" + "5f7374646c696234686f73743232686f73745f646566696e65645f66756e6374696f6e733133666c6f61745f636f6d" + "706172653137683835393637633834333363613334623045040666696e697368071201000f5f5f737461636b5f706f" + "696e746572090a0100072e726f64617461004d0970726f64756365727302086c616e6775616765010452757374000c" + "70726f6365737365642d6279010572757374631d312e38392e30202832393438333838336520323032352d30382d30" + "34290094010f7461726765745f6665617475726573082b0b62756c6b2d6d656d6f72792b0f62756c6b2d6d656d6f72" + "792d6f70742b1663616c6c2d696e6469726563742d6f7665726c6f6e672b0a6d756c746976616c75652b0f6d757461" + "626c652d676c6f62616c732b136e6f6e7472617070696e672d6670746f696e742b0f7265666572656e63652d747970" + "65732b087369676e2d657874"; extern std::string const disabledFloatHex = "0061736d010000000108026000006000017f03030200010503010002063e0a7f004180080b7f004180080b7f004180" diff --git a/src/test/app/wasm_fixtures/float_0/Cargo.lock b/src/test/app/wasm_fixtures/float_0/Cargo.lock new file mode 100644 index 0000000000..4c62a73f26 --- /dev/null +++ b/src/test/app/wasm_fixtures/float_0/Cargo.lock @@ -0,0 +1,171 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "float_0" +version = "0.0.1" +dependencies = [ + "xrpl-wasm-stdlib", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "xrpl-address-macro" +version = "0.7.1" +source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?rev=1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8" +dependencies = [ + "bs58", + "quote", + "sha2", + "syn", +] + +[[package]] +name = "xrpl-wasm-stdlib" +version = "0.7.1" +source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?rev=1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8#1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8" +dependencies = [ + "xrpl-address-macro", +] diff --git a/src/test/app/wasm_fixtures/float_0/Cargo.toml b/src/test/app/wasm_fixtures/float_0/Cargo.toml new file mode 100644 index 0000000000..10b20c5428 --- /dev/null +++ b/src/test/app/wasm_fixtures/float_0/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "float_0" +version = "0.0.1" +edition = "2024" + +# This empty workspace definition keeps this project independent of the parent workspace +[workspace] + +[lib] +crate-type = ["cdylib"] + +[profile.release] +lto = true +opt-level = 's' +panic = "abort" + +[dependencies] +xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", rev = "1e5d096f46742ef7fcf1cb6f28a2526a72ed59d8" } + +[profile.dev] +panic = "abort" diff --git a/src/test/app/wasm_fixtures/float_0/src/lib.rs b/src/test/app/wasm_fixtures/float_0/src/lib.rs new file mode 100644 index 0000000000..2c1e2dd5c3 --- /dev/null +++ b/src/test/app/wasm_fixtures/float_0/src/lib.rs @@ -0,0 +1,57 @@ +#![cfg_attr(target_arch = "wasm32", no_std)] + +use xrpl_std::host::trace::trace; +use xrpl_std::host::{float_compare, float_from_int, float_subtract, FLOAT_ROUNDING_MODES_TO_NEAREST}; + +// Float size constant (8 bytes mantissa + 4 bytes exponent) +const FLOAT_SIZE: usize = 12; + +// FLOAT_ZERO constant +const FLOAT_ZERO: [u8; FLOAT_SIZE] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00]; + +#[unsafe(no_mangle)] +pub extern "C" fn finish() -> i32 { + let _ = trace("\n$$$ test_float_0 $$$"); + + // Test: 10 - 10 should equal 0 + let mut f10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let mut f_result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + + // Create float from 10 + if FLOAT_SIZE as i32 != unsafe { float_from_int(10, f10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) } { + let _ = trace(" float 10-10: failed"); + return 1; + } + + // Subtract: 10 - 10 = 0 + if FLOAT_SIZE as i32 != unsafe { + float_subtract( + f10.as_ptr(), + FLOAT_SIZE, + f10.as_ptr(), + FLOAT_SIZE, + f_result.as_mut_ptr(), + FLOAT_SIZE, + FLOAT_ROUNDING_MODES_TO_NEAREST, + ) + } { + let _ = trace(" float 10-10: failed"); + return 1; + } + + // Compare result with zero + if 0 == unsafe { float_compare(f_result.as_ptr(), FLOAT_SIZE, f_result.as_ptr(), FLOAT_SIZE) } { + let _ = trace(" float 0 compare: good"); + } else { + let _ = trace(" float 0 compare: bad"); + } + + // Compare result with FLOAT_ZERO constant + if 0 == unsafe { float_compare(f_result.as_ptr(), FLOAT_SIZE, FLOAT_ZERO.as_ptr(), FLOAT_SIZE) } { + let _ = trace(" FLOAT_ZERO compare: good"); + } else { + let _ = trace(" FLOAT_ZERO compare: bad"); + } + + 1 +} diff --git a/src/test/app/wasm_fixtures/float_tests/src/lib.rs b/src/test/app/wasm_fixtures/float_tests/src/lib.rs index fc8a1ff5cb..5e4ac5ef7d 100644 --- a/src/test/app/wasm_fixtures/float_tests/src/lib.rs +++ b/src/test/app/wasm_fixtures/float_tests/src/lib.rs @@ -6,15 +6,14 @@ extern crate std; use xrpl_std::core::locator::Locator; -use xrpl_std::core::types::opaque_float::{FLOAT_NEGATIVE_ONE, FLOAT_ONE}; use xrpl_std::decode_hex_32; use xrpl_std::host::trace::DataRepr::AsHex; -use xrpl_std::host::trace::{trace, trace_data, trace_float, trace_num, DataRepr}; +use xrpl_std::host::trace::{trace, trace_data, trace_num, DataRepr}; use xrpl_std::host::{ cache_ledger_obj, float_add, float_compare, float_divide, float_from_int, float_from_uint, float_log, float_multiply, float_pow, float_root, float_set, float_subtract, get_ledger_obj_array_len, get_ledger_obj_field, get_ledger_obj_nested_field, - trace_opaque_float, FLOAT_ROUNDING_MODES_TO_NEAREST, + FLOAT_ROUNDING_MODES_TO_NEAREST, }; use xrpl_std::sfield; use xrpl_std::sfield::{ @@ -22,189 +21,281 @@ use xrpl_std::sfield::{ OwnerCount, PreviousTxnID, PreviousTxnLgrSeq, RegularKey, Sequence, TicketCount, TransferRate, }; -fn test_float_from_wasm() { - let _ = trace("\n$$$ test_float_from_wasm $$$"); +// External host functions not yet in xrpl_std +unsafe extern "C" { + #[link_name = "float_from_stamount"] + fn float_from_stamount( + amount_ptr: *const u8, + amount_len: i32, + out_ptr: *mut u8, + out_len: i32, + rounding: i32, + ) -> i32; - let mut f: [u8; 8] = [0u8; 8]; - if 8 == unsafe { float_from_int(12300, f.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } { + #[link_name = "float_from_stnumber"] + fn float_from_stnumber( + number_ptr: *const u8, + number_len: i32, + out_ptr: *mut u8, + out_len: i32, + rounding: i32, + ) -> i32; + + #[link_name = "float_to_int"] + fn float_to_int( + float_ptr: *const u8, + float_len: i32, + out_ptr: *mut u8, + out_len: i32, + rounding: i32, + ) -> i32; + + #[link_name = "float_to_mantissa_and_exponent"] + fn float_to_mantissa_and_exponent( + float_ptr: *const u8, + float_len: i32, + mantissa_ptr: *mut u8, + mantissa_len: i32, + exponent_ptr: *mut u8, + exponent_len: i32, + ) -> i32; + + #[link_name = "float_negate"] + fn float_negate( + float_ptr: *const u8, + float_len: i32, + out_ptr: *mut u8, + out_len: i32, + ) -> i32; + + #[link_name = "float_abs"] + fn float_abs(float_ptr: *const u8, float_len: i32, out_ptr: *mut u8, out_len: i32) -> i32; +} + +// Float size constant (8 bytes mantissa + 4 bytes exponent) +const FLOAT_SIZE: usize = 12; + +// Float constants (8 bytes mantissa + 4 bytes exponent, big-endian) +// FLOAT_ONE: mantissa=0x0DE0B6B3A7640000 (10^18), exponent=0xFFFFFFEE (-18) +const FLOAT_ONE: [u8; FLOAT_SIZE] = [0x0D, 0xE0, 0xB6, 0xB3, 0xA7, 0x64, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE]; +// FLOAT_NEGATIVE_ONE: mantissa=0xF21F494C589C0000 (-10^18), exponent=0xFFFFFFEE (-18) +const FLOAT_NEGATIVE_ONE: [u8; FLOAT_SIZE] = [0xF2, 0x1F, 0x49, 0x4C, 0x58, 0x9C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xEE]; + +// Helper function to trace floats +fn trace_float(msg: &str, f: &[u8; FLOAT_SIZE]) { + let _ = trace(msg); + let _ = trace_data(" ", f, AsHex); +} + +fn test_float_from_wasm() -> bool { + let _ = trace("\n$$$ test_float_from_wasm $$$"); + let mut all_pass = true; + + let mut f: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + if FLOAT_SIZE as i32 == unsafe { float_from_int(12300, f.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) } { let _ = trace_float(" float from i64 12300:", &f); let _ = trace_data(" float from i64 12300 as HEX:", &f, AsHex); } else { let _ = trace(" float from i64 12300: failed"); + all_pass = false; } let u64_value: u64 = 12300; - if 8 == unsafe { + if FLOAT_SIZE as i32 == unsafe { float_from_uint( &u64_value as *const u64 as *const u8, 8, f.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) } { let _ = trace_float(" float from u64 12300:", &f); } else { let _ = trace(" float from u64 12300: failed"); + all_pass = false; } - if 8 == unsafe { float_set(2, 123, f.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } { + if FLOAT_SIZE as i32 == unsafe { float_set(2, 123, f.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) } { let _ = trace_float(" float from exp 2, mantissa 123:", &f); } else { let _ = trace(" float from exp 2, mantissa 3: failed"); + all_pass = false; } let _ = trace_float(" float from const 1:", &FLOAT_ONE); let _ = trace_float(" float from const -1:", &FLOAT_NEGATIVE_ONE); + + all_pass } -fn test_float_compare() { +fn test_float_compare() -> bool { let _ = trace("\n$$$ test_float_compare $$$"); + let mut all_pass = true; - let mut f1: [u8; 8] = [0u8; 8]; - if 8 != unsafe { float_from_int(1, f1.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } { + let mut f1: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + if FLOAT_SIZE as i32 != unsafe { float_from_int(1, f1.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) } { let _ = trace(" float from 1: failed"); + all_pass = false; } else { let _ = trace_float(" float from 1:", &f1); } - if 0 == unsafe { float_compare(f1.as_ptr(), 8, FLOAT_ONE.as_ptr(), 8) } { + if 0 == unsafe { float_compare(f1.as_ptr(), FLOAT_SIZE, FLOAT_ONE.as_ptr(), FLOAT_SIZE) } { let _ = trace(" float from 1 == FLOAT_ONE"); } else { - let _ = trace(" float from 1 != FLOAT_ONE"); + let _ = trace(" float from 1 != FLOAT_ONE, failed"); + all_pass = false; } - if 1 == unsafe { float_compare(f1.as_ptr(), 8, FLOAT_NEGATIVE_ONE.as_ptr(), 8) } { + if 1 == unsafe { float_compare(f1.as_ptr(), FLOAT_SIZE, FLOAT_NEGATIVE_ONE.as_ptr(), FLOAT_SIZE) } { let _ = trace(" float from 1 > FLOAT_NEGATIVE_ONE"); } else { - let _ = trace(" float from 1 !> FLOAT_NEGATIVE_ONE"); + let _ = trace(" float from 1 !> FLOAT_NEGATIVE_ONE, failed"); + all_pass = false; } - if 2 == unsafe { float_compare(FLOAT_NEGATIVE_ONE.as_ptr(), 8, f1.as_ptr(), 8) } { + if 2 == unsafe { float_compare(FLOAT_NEGATIVE_ONE.as_ptr(), FLOAT_SIZE, f1.as_ptr(), FLOAT_SIZE) } { let _ = trace(" FLOAT_NEGATIVE_ONE < float from 1"); } else { - let _ = trace(" FLOAT_NEGATIVE_ONE !< float from 1"); + let _ = trace(" FLOAT_NEGATIVE_ONE !< float from 1, failed"); + all_pass = false; } + + all_pass } -fn test_float_add_subtract() { +fn test_float_add_subtract() -> bool { let _ = trace("\n$$$ test_float_add_subtract $$$"); + let mut all_pass = true; - let mut f_compute: [u8; 8] = FLOAT_ONE; + let mut f_compute: [u8; FLOAT_SIZE] = FLOAT_ONE; for i in 0..9 { unsafe { float_add( f_compute.as_ptr(), - 8, + FLOAT_SIZE, FLOAT_ONE.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; // let _ = trace_float(" float:", &f_compute); } - let mut f10: [u8; 8] = [0u8; 8]; - if 8 != unsafe { float_from_int(10, f10.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) } { - // let _ = trace(" float from 10: failed"); + let mut f10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + if FLOAT_SIZE as i32 != unsafe { float_from_int(10, f10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) } { + let _ = trace(" float from 10: failed"); + all_pass = false; } - if 0 == unsafe { float_compare(f10.as_ptr(), 8, f_compute.as_ptr(), 8) } { + + if 0 == unsafe { float_compare(f10.as_ptr(), FLOAT_SIZE, f_compute.as_ptr(), FLOAT_SIZE) } { let _ = trace(" repeated add: good"); } else { - let _ = trace(" repeated add: bad"); + let _ = trace(" repeated add: failed"); + all_pass = false; } for i in 0..11 { unsafe { float_subtract( f_compute.as_ptr(), - 8, + FLOAT_SIZE, FLOAT_ONE.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; } - if 0 == unsafe { float_compare(f_compute.as_ptr(), 8, FLOAT_NEGATIVE_ONE.as_ptr(), 8) } { + if 0 == unsafe { float_compare(f_compute.as_ptr(), FLOAT_SIZE, FLOAT_NEGATIVE_ONE.as_ptr(), FLOAT_SIZE) } { let _ = trace(" repeated subtract: good"); } else { - let _ = trace(" repeated subtract: bad"); + let _ = trace(" repeated subtract: failed"); + all_pass = false; } + + all_pass } -fn test_float_multiply_divide() { +fn test_float_multiply_divide() -> bool { let _ = trace("\n$$$ test_float_multiply_divide $$$"); + let mut all_pass = true; - let mut f10: [u8; 8] = [0u8; 8]; - unsafe { float_from_int(10, f10.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) }; - let mut f_compute: [u8; 8] = FLOAT_ONE; + let mut f10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(10, f10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let mut f_compute: [u8; FLOAT_SIZE] = FLOAT_ONE; for i in 0..6 { unsafe { float_multiply( f_compute.as_ptr(), - 8, + FLOAT_SIZE, f10.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; // let _ = trace_float(" float:", &f_compute); } - let mut f1000000: [u8; 8] = [0u8; 8]; + let mut f1000000: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_from_int( 1000000, f1000000.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; - if 0 == unsafe { float_compare(f1000000.as_ptr(), 8, f_compute.as_ptr(), 8) } { + if 0 == unsafe { float_compare(f1000000.as_ptr(), FLOAT_SIZE, f_compute.as_ptr(), FLOAT_SIZE) } { let _ = trace(" repeated multiply: good"); } else { - let _ = trace(" repeated multiply: bad"); + let _ = trace(" repeated multiply: failed"); + all_pass = false; } for i in 0..7 { unsafe { float_divide( f_compute.as_ptr(), - 8, + FLOAT_SIZE, f10.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; } - let mut f01: [u8; 8] = [0u8; 8]; - unsafe { float_set(-1, 1, f01.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let mut f01: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_set(-1, 1, f01.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; - if 0 == unsafe { float_compare(f_compute.as_ptr(), 8, f01.as_ptr(), 8) } { + if 0 == unsafe { float_compare(f_compute.as_ptr(), FLOAT_SIZE, f01.as_ptr(), FLOAT_SIZE) } { let _ = trace(" repeated divide: good"); } else { - let _ = trace(" repeated divide: bad"); + let _ = trace(" repeated divide: failed"); + all_pass = false; } + + all_pass } -fn test_float_pow() { +fn test_float_pow() -> bool { let _ = trace("\n$$$ test_float_pow $$$"); + let mut all_pass = true; - let mut f_compute: [u8; 8] = [0u8; 8]; + let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_pow( FLOAT_ONE.as_ptr(), - 8, + FLOAT_SIZE, 3, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -213,24 +304,24 @@ fn test_float_pow() { unsafe { float_pow( FLOAT_NEGATIVE_ONE.as_ptr(), - 8, + FLOAT_SIZE, 6, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; let _ = trace_float(" float 6th power of -1:", &f_compute); - let mut f9: [u8; 8] = [0u8; 8]; - unsafe { float_from_int(9, f9.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let mut f9: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(9, f9.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; unsafe { float_pow( f9.as_ptr(), - 8, + FLOAT_SIZE, 2, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -239,24 +330,24 @@ fn test_float_pow() { unsafe { float_pow( f9.as_ptr(), - 8, + FLOAT_SIZE, 0, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; let _ = trace_float(" float 0th power of 9:", &f_compute); - let mut f0: [u8; 8] = [0u8; 8]; - unsafe { float_from_int(0, f0.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let mut f0: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(0, f0.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; unsafe { float_pow( f0.as_ptr(), - 8, + FLOAT_SIZE, 2, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -265,10 +356,10 @@ fn test_float_pow() { let r = unsafe { float_pow( f0.as_ptr(), - 8, + FLOAT_SIZE, 0, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -276,21 +367,24 @@ fn test_float_pow() { " float 0th power of 0 (expecting INVALID_PARAMS error):", r as i64, ); + + all_pass } -fn test_float_root() { +fn test_float_root() -> bool { let _ = trace("\n$$$ test_float_root $$$"); + let mut all_pass = true; - let mut f9: [u8; 8] = [0u8; 8]; - unsafe { float_from_int(9, f9.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) }; - let mut f_compute: [u8; 8] = [0u8; 8]; + let mut f9: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(9, f9.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_root( f9.as_ptr(), - 8, + FLOAT_SIZE, 2, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -298,31 +392,31 @@ fn test_float_root() { unsafe { float_root( f9.as_ptr(), - 8, + FLOAT_SIZE, 3, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; let _ = trace_float(" float cbrt of 9:", &f_compute); - let mut f1000000: [u8; 8] = [0u8; 8]; + let mut f1000000: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_from_int( 1000000, f1000000.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; unsafe { float_root( f1000000.as_ptr(), - 8, + FLOAT_SIZE, 3, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -330,96 +424,107 @@ fn test_float_root() { unsafe { float_root( f1000000.as_ptr(), - 8, + FLOAT_SIZE, 6, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; let _ = trace_float(" float 6th root of 1000000:", &f_compute); + + all_pass } -fn test_float_log() { +fn test_float_log() -> bool { let _ = trace("\n$$$ test_float_log $$$"); + let mut all_pass = true; - let mut f1000000: [u8; 8] = [0u8; 8]; + let mut f1000000: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_from_int( 1000000, f1000000.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; - let mut f_compute: [u8; 8] = [0u8; 8]; + let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_log( f1000000.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; let _ = trace_float(" log_10 of 1000000:", &f_compute); + + all_pass } -fn test_float_negate() { +fn test_float_negate() -> bool { let _ = trace("\n$$$ test_float_negate $$$"); + let mut all_pass = true; - let mut f_compute: [u8; 8] = [0u8; 8]; + let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; unsafe { float_multiply( FLOAT_ONE.as_ptr(), - 8, + FLOAT_SIZE, FLOAT_NEGATIVE_ONE.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; // let _ = trace_float(" float:", &f_compute); - if 0 == unsafe { float_compare(FLOAT_NEGATIVE_ONE.as_ptr(), 8, f_compute.as_ptr(), 8) } { + if 0 == unsafe { float_compare(FLOAT_NEGATIVE_ONE.as_ptr(), FLOAT_SIZE, f_compute.as_ptr(), FLOAT_SIZE) } { let _ = trace(" negate const 1: good"); } else { - let _ = trace(" negate const 1: bad"); + let _ = trace(" negate const 1: failed"); + all_pass = false; } unsafe { float_multiply( FLOAT_NEGATIVE_ONE.as_ptr(), - 8, + FLOAT_SIZE, FLOAT_NEGATIVE_ONE.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; // let _ = trace_float(" float:", &f_compute); - if 0 == unsafe { float_compare(FLOAT_ONE.as_ptr(), 8, f_compute.as_ptr(), 8) } { + if 0 == unsafe { float_compare(FLOAT_ONE.as_ptr(), FLOAT_SIZE, f_compute.as_ptr(), FLOAT_SIZE) } { let _ = trace(" negate const -1: good"); } else { - let _ = trace(" negate const -1: bad"); + let _ = trace(" negate const -1: failed"); + all_pass = false; } + + all_pass } -fn test_float_invert() { +fn test_float_invert() -> bool { let _ = trace("\n$$$ test_float_invert $$$"); + let mut all_pass = true; - let mut f_compute: [u8; 8] = [0u8; 8]; - let mut f10: [u8; 8] = [0u8; 8]; - unsafe { float_from_int(10, f10.as_mut_ptr(), 8, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let mut f10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(10, f10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; unsafe { float_divide( FLOAT_ONE.as_ptr(), - 8, + FLOAT_SIZE, f10.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; @@ -427,35 +532,686 @@ fn test_float_invert() { unsafe { float_divide( FLOAT_ONE.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_ptr(), - 8, + FLOAT_SIZE, f_compute.as_mut_ptr(), - 8, + FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST, ) }; let _ = trace_float(" invert again:", &f_compute); // if f10's value is 7, then invert twice won't match the original value - if 0 == unsafe { float_compare(f10.as_ptr(), 8, f_compute.as_ptr(), 8) } { + if 0 == unsafe { float_compare(f10.as_ptr(), FLOAT_SIZE, f_compute.as_ptr(), FLOAT_SIZE) } { let _ = trace(" invert twice: good"); } else { - let _ = trace(" invert twice: bad"); + let _ = trace(" invert twice: failed"); + all_pass = false; } + + all_pass +} + +fn test_float_to_int() -> bool { + let _ = trace("\n$$$ test_float_to_int $$$"); + let mut all_pass = true; + let mut result: [u8; 8] = [0u8; 8]; + + // Test converting FLOAT_ONE (value 1) to int + let ret = unsafe { + float_to_int( + FLOAT_ONE.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + 8, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 { + let number = i64::from_le_bytes(result); + if number == 1 { + let _ = trace(" float_to_int(1): good"); + } else { + let _ = trace(" float_to_int(1): failed"); + let _ = trace_num(" got:", number); + all_pass = false; + } + } else { + let _ = trace(" float_to_int(1): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test converting FLOAT_NEGATIVE_ONE (value -1) to int + let ret = unsafe { + float_to_int( + FLOAT_NEGATIVE_ONE.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + 8, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 { + let number = i64::from_le_bytes(result); + if number == -1 { + let _ = trace(" float_to_int(-1): good"); + } else { + let _ = trace(" float_to_int(-1): failed"); + let _ = trace_num(" got:", number); + all_pass = false; + } + } else { + let _ = trace(" float_to_int(-1): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test converting a larger number (i64::MAX) + let test_val: i64 = i64::MAX; + let mut f_max: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(test_val, f_max.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let ret = unsafe { + float_to_int( + f_max.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + 8, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 { + let number = i64::from_le_bytes(result); + if number == test_val { + let _ = trace(" float_to_int(i64::MAX): good"); + } else { + let _ = trace(" float_to_int(i64::MAX): failed"); + let _ = trace_num(" expected:", test_val); + let _ = trace_num(" got:", number); + all_pass = false; + } + } else { + let _ = trace(" float_to_int(i64::MAX): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test converting zero + let mut f0: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(0, f0.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let ret = unsafe { + float_to_int( + f0.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + 8, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 { + let number = i64::from_le_bytes(result); + if number == 0 { + let _ = trace(" float_to_int(0): good"); + } else { + let _ = trace(" float_to_int(0): failed"); + let _ = trace_num(" got:", number); + all_pass = false; + } + } else { + let _ = trace(" float_to_int(0): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test rounding with fractional value (0.1) + let mut f01: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_set(-1, 1, f01.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + let ret = unsafe { + float_to_int( + f01.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + 8 as i32, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 as i32 { + let number = i64::from_le_bytes(result); + if number == 0 { + let _ = trace(" float_to_int(0.1, to_nearest): good"); + } else { + let _ = trace(" float_to_int(0.1, to_nearest): failed"); + let _ = trace_num(" got:", number); + all_pass = false; + } + } else { + let _ = trace(" float_to_int(0.1, to_nearest): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test rounding mode 1 (towards_zero) + let ret = unsafe { + float_to_int( + f01.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + 8 as i32, + 1 + ) + }; + if ret == 8 as i32 { + let number = i64::from_le_bytes(result); + if number == 0 { + let _ = trace(" float_to_int(0.1, towards_zero): good"); + } else { + let _ = trace(" float_to_int(0.1, towards_zero): failed"); + let _ = trace_num(" got:", number); + all_pass = false; + } + } else { + let _ = trace(" float_to_int(0.1, towards_zero): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + all_pass +} + +fn test_float_to_mantissa_and_exponent() -> bool { + let _ = trace("\n$$$ test_float_to_mantissa_and_exponent $$$"); + let mut all_pass = true; + + // Test with FLOAT_ONE (value 1) + let mut mantissa_bytes: [u8; 8] = [0u8; 8]; + let mut exponent_bytes: [u8; 4] = [0u8; 4]; + let result = unsafe { + float_to_mantissa_and_exponent( + FLOAT_ONE.as_ptr(), + FLOAT_SIZE as i32, + mantissa_bytes.as_mut_ptr(), + 8, + exponent_bytes.as_mut_ptr(), + 4, + ) + }; + + if result == FLOAT_SIZE as i32 { + let mantissa = i64::from_le_bytes(mantissa_bytes); + let exponent = i32::from_le_bytes(exponent_bytes); + if mantissa == 1000000000000000000 && exponent == -18 { + let _ = trace(" float_to_mantissa_and_exponent(1): good"); + } else { + let _ = trace(" float_to_mantissa_and_exponent(1): failed"); + let _ = trace_num(" expected mantissa 1000000000000000000, got:", mantissa); + let _ = trace_num(" expected exponent -18, got:", exponent as i64); + all_pass = false; + } + } else { + let _ = trace(" float_to_mantissa_and_exponent(1): failed with error"); + let _ = trace_num(" error code:", result as i64); + all_pass = false; + } + + // Test with FLOAT_NEGATIVE_ONE (value -1) + let mut mantissa_bytes: [u8; 8] = [0u8; 8]; + let mut exponent_bytes: [u8; 4] = [0u8; 4]; + let result = unsafe { + float_to_mantissa_and_exponent( + FLOAT_NEGATIVE_ONE.as_ptr(), + FLOAT_SIZE as i32, + mantissa_bytes.as_mut_ptr(), + 8, + exponent_bytes.as_mut_ptr(), + 4, + ) + }; + + if result == FLOAT_SIZE as i32 { + let mantissa = i64::from_le_bytes(mantissa_bytes); + let exponent = i32::from_le_bytes(exponent_bytes); + if mantissa == -1000000000000000000 && exponent == -18 { + let _ = trace(" float_to_mantissa_and_exponent(-1): good"); + } else { + let _ = trace(" float_to_mantissa_and_exponent(-1): failed"); + let _ = trace_num(" expected mantissa -1000000000000000000, got:", mantissa); + let _ = trace_num(" expected exponent -18, got:", exponent as i64); + all_pass = false; + } + } else { + let _ = trace(" float_to_mantissa_and_exponent(-1): failed with error"); + let _ = trace_num(" error code:", result as i64); + all_pass = false; + } + + // Test with a float created from int (10) + let mut f10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(10, f10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + + let mut mantissa_bytes: [u8; 8] = [0u8; 8]; + let mut exponent_bytes: [u8; 4] = [0u8; 4]; + let result = unsafe { + float_to_mantissa_and_exponent( + f10.as_ptr(), + FLOAT_SIZE as i32, + mantissa_bytes.as_mut_ptr(), + 8, + exponent_bytes.as_mut_ptr(), + 4, + ) + }; + + if result == FLOAT_SIZE as i32 { + let mantissa = i64::from_le_bytes(mantissa_bytes); + let exponent = i32::from_le_bytes(exponent_bytes); + if mantissa == 1000000000000000000 && exponent == -17 { + let _ = trace(" float_to_mantissa_and_exponent(10): good"); + } else { + let _ = trace(" float_to_mantissa_and_exponent(10): failed"); + let _ = trace_num(" expected mantissa 1000000000000000000, got:", mantissa); + let _ = trace_num(" expected exponent -17, got:", exponent as i64); + all_pass = false; + } + } else { + let _ = trace(" float_to_mantissa_and_exponent(10): failed with error"); + let _ = trace_num(" error code:", result as i64); + all_pass = false; + } + + // Test with zero + let mut f0: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(0, f0.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + + let mut mantissa_bytes: [u8; 8] = [0u8; 8]; + let mut exponent_bytes: [u8; 4] = [0u8; 4]; + let result = unsafe { + float_to_mantissa_and_exponent( + f0.as_ptr(), + FLOAT_SIZE as i32, + mantissa_bytes.as_mut_ptr(), + 8, + exponent_bytes.as_mut_ptr(), + 4, + ) + }; + + if result == FLOAT_SIZE as i32 { + let mantissa = i64::from_le_bytes(mantissa_bytes); + let exponent = i32::from_le_bytes(exponent_bytes); + if mantissa == 0 && exponent == -2147483648 { + let _ = trace(" float_to_mantissa_and_exponent(0): good"); + } else { + let _ = trace(" float_to_mantissa_and_exponent(0): failed"); + let _ = trace_num(" expected mantissa 0, got:", mantissa); + let _ = trace_num(" expected exponent -2147483648, got:", exponent as i64); + all_pass = false; + } + } else { + let _ = trace(" float_to_mantissa_and_exponent(0): failed with error"); + let _ = trace_num(" error code:", result as i64); + all_pass = false; + } + + all_pass +} + +fn test_float_negate_host() -> bool { + let _ = trace("\n$$$ test_float_negate $$$"); + let mut all_pass = true; + + // Test with FLOAT_ONE (value 1) -> should become -1 + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = unsafe { + float_negate( + FLOAT_ONE.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + FLOAT_SIZE as i32, + ) + }; + + if ret == FLOAT_SIZE as i32 { + if result == FLOAT_NEGATIVE_ONE { + let _ = trace(" float_negate(1): good"); + } else { + let _ = trace(" float_negate(1): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_negate(1): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test with FLOAT_NEGATIVE_ONE (value -1) -> should become 1 + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = unsafe { + float_negate( + FLOAT_NEGATIVE_ONE.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + FLOAT_SIZE as i32, + ) + }; + + if ret == FLOAT_SIZE as i32 { + if result == FLOAT_ONE { + let _ = trace(" float_negate(-1): good"); + } else { + let _ = trace(" float_negate(-1): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_negate(-1): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test with zero -> should remain zero + let mut f0: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(0, f0.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = unsafe { + float_negate(f0.as_ptr(), FLOAT_SIZE as i32, result.as_mut_ptr(), FLOAT_SIZE as i32) + }; + + if ret == FLOAT_SIZE as i32 { + if result == f0 { + let _ = trace(" float_negate(0): good"); + } else { + let _ = trace(" float_negate(0): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_negate(0): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + all_pass +} + +fn test_float_abs_host() -> bool { + let _ = trace("\n$$$ test_float_abs $$$"); + let mut all_pass = true; + + // Test with FLOAT_ONE (value 1) -> should remain 1 + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = unsafe { + float_abs( + FLOAT_ONE.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + FLOAT_SIZE as i32, + ) + }; + + if ret == FLOAT_SIZE as i32 { + if result == FLOAT_ONE { + let _ = trace(" float_abs(1): good"); + } else { + let _ = trace(" float_abs(1): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_abs(1): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test with FLOAT_NEGATIVE_ONE (value -1) -> should become 1 + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = unsafe { + float_abs( + FLOAT_NEGATIVE_ONE.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + FLOAT_SIZE as i32, + ) + }; + + if ret == FLOAT_SIZE as i32 { + if result == FLOAT_ONE { + let _ = trace(" float_abs(-1): good"); + } else { + let _ = trace(" float_abs(-1): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_abs(-1): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test with zero -> should remain zero + let mut f0: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(0, f0.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = + unsafe { float_abs(f0.as_ptr(), FLOAT_SIZE as i32, result.as_mut_ptr(), FLOAT_SIZE as i32) }; + + if ret == FLOAT_SIZE as i32 { + if result == f0 { + let _ = trace(" float_abs(0): good"); + } else { + let _ = trace(" float_abs(0): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_abs(0): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + // Test with negative value -> should become positive + let mut f_neg10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(-10, f_neg10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + + let mut f_pos10: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + unsafe { float_from_int(10, f_pos10.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) }; + + let mut result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let ret = unsafe { + float_abs( + f_neg10.as_ptr(), + FLOAT_SIZE as i32, + result.as_mut_ptr(), + FLOAT_SIZE as i32, + ) + }; + + if ret == FLOAT_SIZE as i32 { + if result == f_pos10 { + let _ = trace(" float_abs(-10): good"); + } else { + let _ = trace(" float_abs(-10): failed - result mismatch"); + all_pass = false; + } + } else { + let _ = trace(" float_abs(-10): failed with error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + + all_pass +} + +fn test_float_from_stamount() -> bool { + let _ = trace("\n$$$ test_float_from_stamount $$$"); + let mut all_pass = true; + + // STAmount is serialized as: + // - 1 byte: type/flags + // - 8 bytes: amount (for XRP) or mantissa (for IOU) + // - For IOU: additional currency and issuer fields + + // Create an XRP amount: 100 XRP = 100,000,000 drops + // XRP format: bit 62 clear (not IOU), bit 63 clear (not negative) + // Amount in drops: 100,000,000 = 0x05F5E100 + let xrp_amount: [u8; 8] = [ + 0x40, 0x00, 0x00, 0x00, 0x05, 0xF5, 0xE1, 0x00 + ]; + + let mut f_result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let result_size = unsafe { + float_from_stamount( + xrp_amount.as_ptr(), + 8, + f_result.as_mut_ptr(), + FLOAT_SIZE as i32, + FLOAT_ROUNDING_MODES_TO_NEAREST, + ) + }; + + if result_size == FLOAT_SIZE as i32 { + let _ = trace_float(" float from XRP amount (100 XRP):", &f_result); + + // Convert back to int to verify + let mut int_bytes: [u8; 8] = [0u8; 8]; + let ret = unsafe { + float_to_int( + f_result.as_ptr(), + FLOAT_SIZE as i32, + int_bytes.as_mut_ptr(), + 8, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 { + let int_val = i64::from_le_bytes(int_bytes); + if int_val == 100000000 { + let _ = trace(" XRP amount conversion: good"); + } else { + let _ = trace(" XRP amount conversion: failed"); + let _ = trace_num(" expected 100000000, got:", int_val); + all_pass = false; + } + } else { + let _ = trace(" XRP amount conversion: failed - float_to_int error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + } else { + let _ = trace(" float from XRP amount: failed"); + let _ = trace_num(" result_size:", result_size as i64); + all_pass = false; + } + + all_pass +} + +fn test_float_from_stnumber() -> bool { + let _ = trace("\n$$$ test_float_from_stnumber $$$"); + let mut all_pass = true; + + // STNumber is serialized as: + // - 8 bytes: mantissa (big-endian signed int64) + // - 4 bytes: exponent (big-endian signed int32) + + // Create STNumber for value 123 (mantissa=123*10^18, exponent=-18) + // mantissa = 123000000000000000000 = 0x6ADF37F675EF6B28000 + // But we need to fit in int64, so use mantissa=123*10^15, exponent=-15 + // 123*10^15 = 123000000000000000 = 0x01B69B4BA630F34000 + let stnumber_123: [u8; 12] = [ + 0x01, 0xB6, 0x9B, 0x4B, 0xA6, 0x30, 0xF3, 0x40, // mantissa + 0xFF, 0xFF, 0xFF, 0xF1, // exponent = -15 + ]; + + let mut f_result: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE]; + let result_size = unsafe { + float_from_stnumber( + stnumber_123.as_ptr(), + 12, + f_result.as_mut_ptr(), + FLOAT_SIZE as i32, + FLOAT_ROUNDING_MODES_TO_NEAREST, + ) + }; + + if result_size == FLOAT_SIZE as i32 { + let _ = trace_float(" float from STNumber (123):", &f_result); + + // Convert back to int to verify + let mut int_bytes: [u8; 8] = [0u8; 8]; + let ret = unsafe { + float_to_int( + f_result.as_ptr(), + FLOAT_SIZE as i32, + int_bytes.as_mut_ptr(), + 8, + FLOAT_ROUNDING_MODES_TO_NEAREST + ) + }; + if ret == 8 { + let int_val = i64::from_le_bytes(int_bytes); + if int_val == 123 { + let _ = trace(" STNumber conversion: good"); + } else { + let _ = trace(" STNumber conversion: failed"); + let _ = trace_num(" expected 123, got:", int_val); + all_pass = false; + } + } else { + let _ = trace(" STNumber conversion: failed - float_to_int error"); + let _ = trace_num(" error code:", ret as i64); + all_pass = false; + } + } else { + let _ = trace(" float from STNumber: failed"); + let _ = trace_num(" result_size:", result_size as i64); + all_pass = false; + } + + // Test with FLOAT_ONE constant (which is already in STNumber format) + let result_size = unsafe { + float_from_stnumber( + FLOAT_ONE.as_ptr(), + FLOAT_SIZE as i32, + f_result.as_mut_ptr(), + FLOAT_SIZE as i32, + FLOAT_ROUNDING_MODES_TO_NEAREST, + ) + }; + + if result_size == FLOAT_SIZE as i32 { + let _ = trace_float(" float from STNumber (1):", &f_result); + + // Should match FLOAT_ONE + if 0 == unsafe { float_compare(f_result.as_ptr(), FLOAT_SIZE, FLOAT_ONE.as_ptr(), FLOAT_SIZE) } { + let _ = trace(" STNumber(1) == FLOAT_ONE: good"); + } else { + let _ = trace(" STNumber(1) == FLOAT_ONE: failed"); + all_pass = false; + } + } else { + let _ = trace(" float from STNumber(1): failed"); + all_pass = false; + } + + all_pass } #[unsafe(no_mangle)] pub extern "C" fn finish() -> i32 { - test_float_from_wasm(); - test_float_compare(); - test_float_add_subtract(); - test_float_multiply_divide(); - test_float_pow(); - test_float_root(); - test_float_log(); - test_float_negate(); - test_float_invert(); + let mut all_pass = true; + all_pass &= test_float_from_wasm(); + all_pass &= test_float_compare(); + all_pass &= test_float_add_subtract(); + all_pass &= test_float_multiply_divide(); + all_pass &= test_float_pow(); + all_pass &= test_float_root(); + all_pass &= test_float_log(); + all_pass &= test_float_negate(); + all_pass &= test_float_invert(); + all_pass &= test_float_to_int(); + all_pass &= test_float_to_mantissa_and_exponent(); + all_pass &= test_float_negate_host(); + all_pass &= test_float_abs_host(); + all_pass &= test_float_from_stamount(); + all_pass &= test_float_from_stnumber(); - 1 + if all_pass { 1 } else { 0 } } diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 0079b540b0..0540c13afd 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -1568,13 +1568,15 @@ public: {Number{1'000'000'000'000'000ll}, Number{15}}, {Number{5625, -4}, Number{-2'498'774'732'165'998, -16}}}); - auto const cLarge = std::to_array( - {{Number{false, Number::maxMantissa() - 9, -1, Number::normalized{}}, - Number{false, 1'746'901'684'478'673'451ll, -17, Number::normalized{}}}, - {Number{false, Number::maxMantissa() - 9, 0, Number::normalized{}}, - Number{false, 1'846'901'684'478'673'451ll, -17, Number::normalized{}}}, - {Number{Number::maxRep}, - Number{false, 1'861'728'612'932'620'011ll, -17, Number::normalized{}}}}); + auto const cLarge = std::to_array({ + {Number{Number::maxMantissa() - 9, -1, Number::normalized{}}, + Number{1'799'999'999'999'999'999ll, -17}}, + {Number{Number::maxMantissa() - 9, 0, Number::normalized{}}, + Number{1'899'999'999'999'999'999ll, -17}}, + {Number{Number::maxRep, 0, Number::normalized{}}, + Number{1'896'488'972'683'081'529ll, -17}}, + {Number{999'999'999'999'999'999ll}, Number{1'799'999'999'999'999'999, -17}}, + }); if (Number::getMantissaScale() == MantissaRange::small) {