Refactoring float hostfunctions (#7053)

This commit is contained in:
Olek
2026-05-07 12:33:22 -04:00
committed by GitHub
parent 1600b3e7f3
commit 597202a6f0
17 changed files with 2030 additions and 2031 deletions

View File

@@ -381,9 +381,6 @@ 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
@@ -732,10 +729,6 @@ abs(Number x) noexcept
Number
power(Number const& f, unsigned n);
// logarithm with base 10
Number
log10(Number const& value, int iterations = 50);
// Returns f^(1/d)
// Uses NewtonRaphson iterations until the result stops changing
// to find the root of the polynomial g(x) = x^d - f

View File

@@ -66,16 +66,10 @@ Expected<int64_t, HostFunctionError>
floatToIntImpl(Slice const& x, int32_t mode);
Expected<FloatPair, HostFunctionError>
floatToMantissaAndExponentImpl(Slice const& x);
floatToMantExpImpl(Slice const& x);
Expected<Bytes, HostFunctionError>
floatNegateImpl(Slice const& x);
Expected<Bytes, HostFunctionError>
floatAbsImpl(Slice const& x);
Expected<Bytes, HostFunctionError>
floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode);
floatFromMantExpImpl(int64_t mantissa, int32_t exponent, int32_t mode);
Expected<int32_t, HostFunctionError>
floatCompareImpl(Slice const& x, Slice const& y);
@@ -98,9 +92,6 @@ floatRootImpl(Slice const& x, int32_t n, int32_t mode);
Expected<Bytes, HostFunctionError>
floatPowerImpl(Slice const& x, int32_t n, int32_t mode);
Expected<Bytes, HostFunctionError>
floatLogImpl(Slice const& x, int32_t mode);
} // namespace wasm_float
// Intended to work only through wasm runtime. Don't call them directly, except with unit tests
@@ -480,25 +471,13 @@ struct HostFunctions
}
virtual Expected<FloatPair, HostFunctionError>
floatToMantissaAndExponent(Slice const& x) const
floatToMantExp(Slice const& x) const
{
return Unexpected(HostFunctionError::INTERNAL);
}
virtual Expected<Bytes, HostFunctionError>
floatNegate(Slice const& x) const
{
return Unexpected(HostFunctionError::INTERNAL);
}
virtual Expected<Bytes, HostFunctionError>
floatAbs(Slice const& x) const
{
return Unexpected(HostFunctionError::INTERNAL);
}
virtual Expected<Bytes, HostFunctionError>
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const
{
return Unexpected(HostFunctionError::INTERNAL);
}
@@ -545,12 +524,6 @@ struct HostFunctions
return Unexpected(HostFunctionError::INTERNAL);
}
virtual Expected<Bytes, HostFunctionError>
floatLog(Slice const& x, int32_t mode) const
{
return Unexpected(HostFunctionError::INTERNAL);
}
virtual ~HostFunctions() = default;
// LCOV_EXCL_STOP
};

View File

@@ -266,16 +266,10 @@ public:
floatToInt(Slice const& x, int32_t mode) const override;
Expected<FloatPair, HostFunctionError>
floatToMantissaAndExponent(Slice const& x) const override;
floatToMantExp(Slice const& x) const override;
Expected<Bytes, HostFunctionError>
floatNegate(Slice const& x) const override;
Expected<Bytes, HostFunctionError>
floatAbs(Slice const& x) const override;
Expected<Bytes, HostFunctionError>
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override;
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const override;
Expected<int32_t, HostFunctionError>
floatCompare(Slice const& x, Slice const& y) const override;
@@ -297,9 +291,6 @@ public:
Expected<Bytes, HostFunctionError>
floatPower(Slice const& x, int32_t n, int32_t mode) const override;
Expected<Bytes, HostFunctionError>
floatLog(Slice const& x, int32_t mode) const override;
};
} // namespace xrpl

View File

@@ -276,22 +276,13 @@ using floatToInt_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int
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);
using floatToMantExp_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);
floatToMantExp_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);
using floatFromMantExp_proto = int32_t(int64_t, int32_t, uint8_t*, int32_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);
floatFromMantExp_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
using floatCompare_proto = int32_t(uint8_t const*, int32_t, uint8_t const*, int32_t);
wasm_trap_t*
@@ -325,8 +316,4 @@ using floatPower_proto = int32_t(uint8_t const*, int32_t, int32_t, uint8_t*, int
wasm_trap_t*
floatPower_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
using floatLog_proto = int32_t(uint8_t const*, int32_t, uint8_t*, int32_t, int32_t);
wasm_trap_t*
floatLog_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
} // namespace xrpl

View File

@@ -52,7 +52,7 @@ enum WasmTypes { WT_I32, WT_I64 };
struct WasmImportFunc
{
std::string name;
std::string_view name;
std::optional<WasmTypes> result;
std::vector<WasmTypes> params;
// void* udata = nullptr;
@@ -62,11 +62,12 @@ struct WasmImportFunc
};
typedef std::pair<void*, WasmImportFunc> WasmUserData;
typedef std::vector<WasmUserData> ImportVec;
typedef std::unordered_map<std::string_view, WasmUserData> ImportVec;
#define WASM_IMPORT_FUNC(v, f, ...) \
WasmImpFunc<f##_proto>(v, #f, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
// n - string literal name, must have static lifetime
#define WASM_IMPORT_FUNC2(v, f, n, ...) \
WasmImpFunc<f##_proto>(v, n, reinterpret_cast<void*>(&f##_wrap), ##__VA_ARGS__)
@@ -123,6 +124,7 @@ WasmImpFuncHelper(WasmImportFunc& e)
// WasmImpWrap(e, std::forward<F>(f));
}
// imp_name - string literal, must have static lifetime
template <typename F>
void
WasmImpFunc(
@@ -137,7 +139,7 @@ WasmImpFunc(
e.wrap = f_wrap;
e.gas = gas;
WasmImpFuncHelper<F>(e);
v.push_back(std::make_pair(data, std::move(e)));
v.emplace(imp_name, std::make_pair(data, std::move(e)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -945,82 +945,6 @@ power(Number const& f, unsigned n)
return r;
}
// Series expansion method approximation of ln(x)
static Number
ln(Number const& x, int iterations = 50)
{
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)
{
throw std::runtime_error("Not a positive value");
}
if (x == 1)
{
return N0;
}
int exponent = 0;
Number mantissa = x;
while (mantissa >= N2)
{
mantissa /= 2;
exponent += 1;
}
while (mantissa < N05)
{
mantissa *= 2;
exponent -= 1;
}
Number z = (mantissa - 1) / (mantissa + 1);
Number const zz = z * z;
Number sum;
for (int i = 1; i <= iterations; ++i)
{
sum = sum + z / ((2 * i) - 1);
z = z * zz;
}
return 2 * sum + exponent * LN2;
}
Number
log10(Number const& x, int iterations)
{
Number const N0(0);
Number const LN10(2'302'585'092'994'045'684ll, -18);
if (x <= 0)
{
throw std::runtime_error("Not a positive value");
}
if (x == 1)
{
return N0;
}
if (x <= Number(10))
{
auto const r = ln(x, iterations) / LN10;
return r;
}
// (1 <= normalX < 10)
// x = normalX * 10^norm
// ln(x) = ln(normalX * 10^norm) = ln(normalX) + norm * ln(10)
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;
return lgX;
}
// Returns f^(1/d)
// Uses NewtonRaphson iterations until the result stops changing
// to find the non-negative root of the polynomial g(x) = x^d - f

View File

@@ -259,7 +259,7 @@ floatToIntImpl(Slice const& x, int32_t mode)
}
Expected<FloatPair, HostFunctionError>
floatToMantissaAndExponentImpl(Slice const& x)
floatToMantExpImpl(Slice const& x)
{
try
{
@@ -282,55 +282,7 @@ floatToMantissaAndExponentImpl(Slice const& x)
}
Expected<Bytes, HostFunctionError>
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<Bytes, HostFunctionError>
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
}
Expected<Bytes, HostFunctionError>
floatSetImpl(int64_t mantissa, int32_t exponent, int32_t mode)
floatFromMantExpImpl(int64_t mantissa, int32_t exponent, int32_t mode)
{
try
{
@@ -537,31 +489,6 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode)
// LCOV_EXCL_STOP
}
Expected<Bytes, HostFunctionError>
floatLogImpl(Slice const& x, int32_t mode)
{
try
{
detail::FloatState const rm(mode);
if (!rm)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
detail::WasmNumber const xx(x);
if (!xx)
return Unexpected(HostFunctionError::FLOAT_INPUT_MALFORMED);
detail::WasmNumber const res(log10(xx));
return res.toBytes();
}
// LCOV_EXCL_START
catch (...)
{
return Unexpected(HostFunctionError::FLOAT_COMPUTATION_ERROR);
}
// LCOV_EXCL_STOP
}
} // namespace wasm_float
// =========================================================
@@ -599,27 +526,15 @@ WasmHostFunctionsImpl::floatToInt(Slice const& x, int32_t mode) const
}
Expected<FloatPair, HostFunctionError>
WasmHostFunctionsImpl::floatToMantissaAndExponent(Slice const& x) const
WasmHostFunctionsImpl::floatToMantExp(Slice const& x) const
{
return wasm_float::floatToMantissaAndExponentImpl(x);
return wasm_float::floatToMantExpImpl(x);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatNegate(Slice const& x) const
WasmHostFunctionsImpl::floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const
{
return wasm_float::floatNegateImpl(x);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatAbs(Slice const& x) const
{
return wasm_float::floatAbsImpl(x);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const
{
return wasm_float::floatSetImpl(mantissa, exponent, mode);
return wasm_float::floatFromMantExpImpl(mantissa, exponent, mode);
}
Expected<int32_t, HostFunctionError>
@@ -664,10 +579,4 @@ WasmHostFunctionsImpl::floatPower(Slice const& x, int32_t n, int32_t mode) const
return wasm_float::floatPowerImpl(x, n, mode);
}
Expected<Bytes, HostFunctionError>
WasmHostFunctionsImpl::floatLog(Slice const& x, int32_t mode) const
{
return wasm_float::floatLogImpl(x, mode);
}
} // namespace xrpl

View File

@@ -1769,7 +1769,7 @@ floatToInt_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results
}
wasm_trap_t*
floatToMantissaAndExponent_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
floatToMantExp_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
@@ -1782,11 +1782,11 @@ floatToMantissaAndExponent_wrap(void* env, wasm_val_vec_t const* params, wasm_va
return hfResult(results, x.error());
i = 2;
return returnResult(runtime, params, results, hf->floatToMantissaAndExponent(*x), i);
return returnResult(runtime, params, results, hf->floatToMantExp(*x), i);
}
wasm_trap_t*
floatNegate_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
floatFromMantExp_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
@@ -1794,55 +1794,22 @@ floatNegate_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* result
auto* runtime = reinterpret_cast<WasmRuntimeWrapper*>(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* runtime = reinterpret_cast<WasmRuntimeWrapper*>(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)
{
if (auto g = checkGas(env); !g)
return g.error(); // LCOV_EXCL_LINE
auto* hf = getHF(env);
auto* runtime = reinterpret_cast<WasmRuntimeWrapper*>(hf->getRT());
int i = 0;
auto const exp = getDataInt32(runtime, params, i);
if (!exp)
return hfResult(results, exp.error()); // LCOV_EXCL_LINE
auto const mant = getDataInt64(runtime, params, i);
if (!mant)
return hfResult(results, mant.error()); // LCOV_EXCL_LINE
auto const exp = getDataInt32(runtime, params, i);
if (!exp)
return hfResult(results, exp.error()); // LCOV_EXCL_LINE
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->floatSet(*mant, *exp, *rounding), i);
return returnResult(runtime, params, results, hf->floatFromMantExp(*mant, *exp, *rounding), i);
}
wasm_trap_t*
@@ -2021,28 +1988,6 @@ floatPower_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results
return returnResult(runtime, params, results, hf->floatPower(*x, *n, *rounding), i);
}
wasm_trap_t*
floatLog_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* runtime = reinterpret_cast<WasmRuntimeWrapper*>(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->floatLog(*x, *rounding), i);
}
// LCOV_EXCL_START
namespace test {

View File

@@ -79,10 +79,8 @@ setCommonHostFunctions(HostFunctions* hfs, ImportVec& i)
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, floatToMantExp, "float_to_mant_exp", hfs, 130);
WASM_IMPORT_FUNC2(i, floatFromMantExp, "float_from_mant_exp", hfs, 100);
WASM_IMPORT_FUNC2(i, floatCompare, "float_compare", hfs, 80);
WASM_IMPORT_FUNC2(i, floatAdd, "float_add", hfs, 160);
WASM_IMPORT_FUNC2(i, floatSubtract, "float_subtract", hfs, 160);
@@ -90,7 +88,6 @@ setCommonHostFunctions(HostFunctions* hfs, ImportVec& i)
WASM_IMPORT_FUNC2(i, floatDivide, "float_divide", hfs, 300);
WASM_IMPORT_FUNC2(i, floatRoot, "float_root", hfs, 5'500);
WASM_IMPORT_FUNC2(i, floatPower, "float_pow", hfs, 5'500);
WASM_IMPORT_FUNC2(i, floatLog, "float_log", hfs, 12'000);
// clang-format on
}

View File

@@ -368,7 +368,7 @@ ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) const
if (importTypes.empty())
return {};
if (imports.empty())
throw std::runtime_error("Missing imports");
Throw<std::runtime_error>("Empty imports");
WasmExternVec wimports(importTypes.size());
@@ -385,7 +385,7 @@ ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) const
wasm_externkind_t const itype = wasm_externtype_kind(wasm_importtype_type(importType));
if ((itype) != WASM_EXTERN_FUNC)
{
throw std::runtime_error(
Throw<std::runtime_error>(
"Invalid import type " + std::to_string(itype)); // LCOV_EXCL_LINE
}
@@ -393,46 +393,39 @@ ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) const
// if ((W_ENV != modName) && (W_HOST_LIB != modName))
// continue;
bool impSet = false;
for (auto const& obj : imports)
{
auto const& imp = obj.second;
if (imp.name != fieldName)
continue;
WasmValtypeVec params(makeImpParams(imp));
WasmValtypeVec results(makeImpReturn(imp));
std::unique_ptr<wasm_functype_t, decltype(&wasm_functype_delete)> const ftype(
wasm_functype_new(params.get(), results.get()), &wasm_functype_delete);
params.release();
results.release();
wasm_func_t* func = wasm_func_new_with_env(
s.get(),
ftype.get(),
reinterpret_cast<wasm_func_callback_with_env_t>(imp.wrap),
(void*)&obj,
nullptr);
if (func == nullptr)
{
// LCOV_EXCL_START
throw std::runtime_error("can't create import function " + imp.name);
// LCOV_EXCL_STOP
}
wimports[i] = wasm_func_as_extern(func);
++impCnt;
impSet = true;
break;
}
if (!impSet)
auto const it = imports.find(fieldName);
if (it == imports.end())
{
print_wasm_error("Import not found: " + std::string(fieldName), nullptr, j_);
continue; // print all missed import
}
auto const& obj = it->second;
auto const& imp = obj.second;
WasmValtypeVec params(makeImpParams(imp));
WasmValtypeVec results(makeImpReturn(imp));
std::unique_ptr<wasm_functype_t, decltype(&wasm_functype_delete)> const ftype(
wasm_functype_new(params.get(), results.get()), &wasm_functype_delete);
params.release();
results.release();
wasm_func_t* func = wasm_func_new_with_env(
s.get(),
ftype.get(),
reinterpret_cast<wasm_func_callback_with_env_t>(imp.wrap),
(void*)&obj,
nullptr);
if (func == nullptr)
{
Throw<std::runtime_error>(
"can't create import function " + std::string(imp.name)); // LCOV_EXCL_LINE
}
wimports[i] = wasm_func_as_extern(func);
++impCnt;
}
if (impCnt != importTypes.size())
@@ -442,7 +435,7 @@ ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) const
std::to_string(importTypes.size()),
nullptr,
j_);
throw std::runtime_error("Missing imports");
Throw<std::runtime_error>("Missing imports");
}
return wimports;
@@ -741,7 +734,7 @@ checkImports(ImportVec const& imports, HostFunctions* hfs)
{
for (auto const& obj : imports)
{
if (hfs != obj.first)
if (hfs != obj.second.first)
Throw<std::runtime_error>("Imports hf unsync");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -477,27 +477,15 @@ public:
}
virtual Expected<FloatPair, HostFunctionError>
floatToMantissaAndExponent(Slice const& x) const override
floatToMantExp(Slice const& x) const override
{
return wasm_float::floatToMantissaAndExponentImpl(x);
return wasm_float::floatToMantExpImpl(x);
}
Expected<Bytes, HostFunctionError>
floatNegate(Slice const& x) const override
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const override
{
return wasm_float::floatNegateImpl(x);
}
Expected<Bytes, HostFunctionError>
floatAbs(Slice const& x) const override
{
return wasm_float::floatAbsImpl(x);
}
Expected<Bytes, HostFunctionError>
floatSet(int64_t mantissa, int32_t exponent, int32_t mode) const override
{
return wasm_float::floatSetImpl(mantissa, exponent, mode);
return wasm_float::floatFromMantExpImpl(mantissa, exponent, mode);
}
Expected<int32_t, HostFunctionError>
@@ -541,12 +529,6 @@ public:
{
return wasm_float::floatPowerImpl(x, n, mode);
}
Expected<Bytes, HostFunctionError>
floatLog(Slice const& x, int32_t mode) const override
{
return wasm_float::floatLogImpl(x, mode);
}
};
struct TestHostFunctionsSink : public TestHostFunctions

View File

@@ -154,7 +154,7 @@ struct Wasm_test : public beast::unit_test::suite
void
testWasmLib()
{
testcase("wasmtime lib test");
testcase("wasm lib test");
// clang-format off
/* The WASM module buffer. */
Bytes const wasm = {/* WASM header */
@@ -369,7 +369,7 @@ struct Wasm_test : public beast::unit_test::suite
TestHostFunctions hfs(env, 0);
auto imp = createWasmImport(hfs);
for (auto& i : imp)
i.second.gas = 0;
i.second.second.gas = 0;
auto re = engine.run(
allHostFuncWasm, hfs, 1'000'000, ESCROW_FUNCTION_NAME, {}, imp, env.journal);
@@ -498,6 +498,8 @@ struct Wasm_test : public beast::unit_test::suite
checkResult(re, -201, 28'965);
}
// This test use log output, so DEBUG_OUTPUT must be disabled.
#ifndef DEBUG_OUTPUT
{ // fail because recursion too deep
auto const deepWasm = hexToBytes(deepRecursionHex);
@@ -525,6 +527,7 @@ struct Wasm_test : public beast::unit_test::suite
BEAST_EXPECT(countSubstr(s, "WASMI Error: failure to call func") == 1);
BEAST_EXPECT(countSubstr(s, "exception: <finish> failure") > 0);
}
#endif
{ // infinite loop
auto const infiniteLoopWasm = hexToBytes(infiniteLoopWasmHex);
@@ -560,7 +563,7 @@ struct Wasm_test : public beast::unit_test::suite
TestLedgerDataProvider hfs(env);
ImportVec imports;
WASM_IMPORT_FUNC2(imports, getLedgerSqn, "get_ledger_sqn", &hfs);
imports[0].first = nullptr;
imports["get_ledger_sqn"].first = nullptr;
auto& engine = WasmEngine::instance();
@@ -599,7 +602,7 @@ struct Wasm_test : public beast::unit_test::suite
TestHostFunctions hfs(env, 0);
auto re = runEscrowWasm(floatTestWasm, hfs, 200'000, funcName, {});
checkResult(re, 1, 167'965);
checkResult(re, 1, 134'938);
env.close();
}
@@ -625,7 +628,7 @@ struct Wasm_test : public beast::unit_test::suite
auto const codecovWasm = hexToBytes(codecovTestsWasmHex);
TestHostFunctions hfs(env, 0);
auto const allowance = 202'724;
auto const allowance = 208'169;
auto re = runEscrowWasm(codecovWasm, hfs, allowance, ESCROW_FUNCTION_NAME, {});
checkResult(re, 1, allowance);

View File

@@ -1042,21 +1042,6 @@ pub extern "C" fn finish() -> i32 {
"float_pow_oob_slice",
)
});
with_buffer::<2, _, _>(|ptr, len| {
check_result(
unsafe {
host::float_log(
float.as_ptr().wrapping_add(1_000_000_000),
float.len(),
ptr,
len,
FLOAT_ROUNDING_MODES_TO_NEAREST,
)
},
error_codes::POINTER_OUT_OF_BOUNDS,
"float_log_oob_slice",
)
});
// invalid UInt32

File diff suppressed because it is too large Load Diff

View File

@@ -11,7 +11,7 @@ use xrpl_std::host::trace::DataRepr::AsHex;
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,
float_multiply, float_pow, float_root, float_subtract,
get_ledger_obj_array_len, get_ledger_obj_field, get_ledger_obj_nested_field,
FLOAT_ROUNDING_MODES_TO_NEAREST,
};
@@ -50,8 +50,8 @@ unsafe extern "C" {
rounding: i32,
) -> i32;
#[link_name = "float_to_mantissa_and_exponent"]
fn float_to_mantissa_and_exponent(
#[link_name = "float_to_mant_exp"]
fn float_to_mant_exp(
float_ptr: *const u8,
float_len: i32,
mantissa_ptr: *mut u8,
@@ -60,16 +60,14 @@ unsafe extern "C" {
exponent_len: i32,
) -> i32;
#[link_name = "float_negate"]
fn float_negate(
float_ptr: *const u8,
float_len: i32,
#[link_name = "float_from_mant_exp"]
fn float_from_mant_exp(
mantissa: i64,
exponent: i32,
out_ptr: *mut u8,
out_len: i32,
rounding: 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)
@@ -116,10 +114,10 @@ fn test_float_from_wasm() -> bool {
all_pass = false;
}
if FLOAT_SIZE as i32 == unsafe { float_set(2, 123, f.as_mut_ptr(), FLOAT_SIZE, FLOAT_ROUNDING_MODES_TO_NEAREST) } {
if FLOAT_SIZE as i32 == unsafe { float_from_mant_exp(123, 2, f.as_mut_ptr(), FLOAT_SIZE as i32, 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");
let _ = trace(" float from exp 2, mantissa 123: failed");
all_pass = false;
}
@@ -272,7 +270,7 @@ fn test_float_multiply_divide() -> bool {
};
}
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) };
unsafe { float_from_mant_exp(1, -1, f01.as_mut_ptr(), FLOAT_SIZE as i32, FLOAT_ROUNDING_MODES_TO_NEAREST) };
if 0 == unsafe { float_compare(f_compute.as_ptr(), FLOAT_SIZE, f01.as_ptr(), FLOAT_SIZE) } {
let _ = trace(" repeated divide: good");
@@ -436,80 +434,6 @@ fn test_float_root() -> bool {
all_pass
}
fn test_float_log() -> bool {
let _ = trace("\n$$$ test_float_log $$$");
let mut all_pass = true;
let mut f1000000: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE];
unsafe {
float_from_int(
1000000,
f1000000.as_mut_ptr(),
FLOAT_SIZE,
FLOAT_ROUNDING_MODES_TO_NEAREST,
)
};
let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE];
unsafe {
float_log(
f1000000.as_ptr(),
FLOAT_SIZE,
f_compute.as_mut_ptr(),
FLOAT_SIZE,
FLOAT_ROUNDING_MODES_TO_NEAREST,
)
};
let _ = trace_float(" log_10 of 1000000:", &f_compute);
all_pass
}
fn test_float_negate() -> bool {
let _ = trace("\n$$$ test_float_negate $$$");
let mut all_pass = true;
let mut f_compute: [u8; FLOAT_SIZE] = [0u8; FLOAT_SIZE];
unsafe {
float_multiply(
FLOAT_ONE.as_ptr(),
FLOAT_SIZE,
FLOAT_NEGATIVE_ONE.as_ptr(),
FLOAT_SIZE,
f_compute.as_mut_ptr(),
FLOAT_SIZE,
FLOAT_ROUNDING_MODES_TO_NEAREST,
)
};
// let _ = trace_float(" float:", &f_compute);
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: failed");
all_pass = false;
}
unsafe {
float_multiply(
FLOAT_NEGATIVE_ONE.as_ptr(),
FLOAT_SIZE,
FLOAT_NEGATIVE_ONE.as_ptr(),
FLOAT_SIZE,
f_compute.as_mut_ptr(),
FLOAT_SIZE,
FLOAT_ROUNDING_MODES_TO_NEAREST,
)
};
// let _ = trace_float(" float:", &f_compute);
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: failed");
all_pass = false;
}
all_pass
}
fn test_float_invert() -> bool {
let _ = trace("\n$$$ test_float_invert $$$");
let mut all_pass = true;
@@ -666,7 +590,7 @@ fn test_float_to_int() -> bool {
// 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) };
unsafe { float_from_mant_exp(1, -1, f01.as_mut_ptr(), FLOAT_SIZE as i32, FLOAT_ROUNDING_MODES_TO_NEAREST) };
let ret = unsafe {
float_to_int(
f01.as_ptr(),
@@ -719,15 +643,15 @@ fn test_float_to_int() -> bool {
all_pass
}
fn test_float_to_mantissa_and_exponent() -> bool {
let _ = trace("\n$$$ test_float_to_mantissa_and_exponent $$$");
fn test_float_to_mant_exp() -> bool {
let _ = trace("\n$$$ test_float_to_mant_exp $$$");
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_to_mant_exp(
FLOAT_ONE.as_ptr(),
FLOAT_SIZE as i32,
mantissa_bytes.as_mut_ptr(),
@@ -741,15 +665,15 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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");
let _ = trace(" float_to_mant_exp(1): good");
} else {
let _ = trace(" float_to_mantissa_and_exponent(1): failed");
let _ = trace(" float_to_mant_exp(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(" float_to_mant_exp(1): failed with error");
let _ = trace_num(" error code:", result as i64);
all_pass = false;
}
@@ -758,7 +682,7 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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_to_mant_exp(
FLOAT_NEGATIVE_ONE.as_ptr(),
FLOAT_SIZE as i32,
mantissa_bytes.as_mut_ptr(),
@@ -772,15 +696,15 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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");
let _ = trace(" float_to_mant_exp(-1): good");
} else {
let _ = trace(" float_to_mantissa_and_exponent(-1): failed");
let _ = trace(" float_to_mant_exp(-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(" float_to_mant_exp(-1): failed with error");
let _ = trace_num(" error code:", result as i64);
all_pass = false;
}
@@ -792,7 +716,7 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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_to_mant_exp(
f10.as_ptr(),
FLOAT_SIZE as i32,
mantissa_bytes.as_mut_ptr(),
@@ -806,15 +730,15 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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");
let _ = trace(" float_to_mant_exp(10): good");
} else {
let _ = trace(" float_to_mantissa_and_exponent(10): failed");
let _ = trace(" float_to_mant_exp(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(" float_to_mant_exp(10): failed with error");
let _ = trace_num(" error code:", result as i64);
all_pass = false;
}
@@ -826,7 +750,7 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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_to_mant_exp(
f0.as_ptr(),
FLOAT_SIZE as i32,
mantissa_bytes.as_mut_ptr(),
@@ -840,15 +764,15 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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");
let _ = trace(" float_to_mant_exp(0): good");
} else {
let _ = trace(" float_to_mantissa_and_exponent(0): failed");
let _ = trace(" float_to_mant_exp(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(" float_to_mant_exp(0): failed with error");
let _ = trace_num(" error code:", result as i64);
all_pass = false;
}
@@ -856,189 +780,6 @@ fn test_float_to_mantissa_and_exponent() -> bool {
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;
@@ -1203,13 +944,9 @@ pub extern "C" fn finish() -> i32 {
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_to_mant_exp();
all_pass &= test_float_from_stamount();
all_pass &= test_float_from_stnumber();

View File

@@ -1542,78 +1542,6 @@ public:
}
}
void
test_log10()
{
auto const scale = Number::getMantissaScale();
testcase << "test_lg " << to_string(scale);
using Case = std::tuple<Number, Number>;
auto test = [this](auto const& c) {
for (auto const& [x, z] : c)
{
auto const result = log10(x);
std::stringstream ss;
ss << "lg(" << x << ") = " << result << ". Expected: " << z;
// std::cout << ss.str() << std::endl;
BEAST_EXPECTS(result == z, ss.str());
}
};
auto const cSmall = std::to_array<Case>(
{{Number{2}, Number{3'010'299'956'639'811ll, -16}},
{Number{2'000'000}, Number{6'301'029'995'663'985ll, -15}},
{Number{2, -30}, Number{-2'969'897'000'433'602ll, -14}},
{Number{1}, Number{0}},
{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<Case>({
{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)
{
test(cSmall);
}
else
{
NumberRoundModeGuard const mg(Number::towards_zero);
test(cLarge);
}
{
bool caught = false;
try
{
log10(Number{-2});
}
catch (std::runtime_error const&)
{
caught = true;
}
BEAST_EXPECT(caught);
caught = false;
try
{
log10(Number());
}
catch (std::runtime_error const&)
{
caught = true;
}
BEAST_EXPECT(caught);
caught = false;
}
}
void
run() override
{
@@ -1641,7 +1569,6 @@ public:
test_truncate();
testRounding();
testInt64();
test_log10();
}
}
};