diff --git a/src/compile/c_gen.cpp b/src/compile/c_gen.cpp index f5f373b..3166058 100644 --- a/src/compile/c_gen.cpp +++ b/src/compile/c_gen.cpp @@ -3603,7 +3603,7 @@ std::string CGen::TryCompileBuiltinMathCall(const std::shared_ptr 全范围随机整数(拼 4 个 rand 填满 64 位,跨平台一致) // random(<0) -> 抛异常 "interval is empty" @@ -3693,11 +3694,12 @@ std::string CGen::TryCompileBuiltinMathCall(const std::shared_ptr u -> 抛异常 "interval is empty" // l == u -> 返回 l @@ -3731,9 +3733,10 @@ std::string CGen::TryCompileBuiltinMathCall(const std::shared_ptr 2147483647LL) exponent = 2147483647LL; + if (exponent < (-2147483647LL - 1)) exponent = (-2147483647LL - 1); + return ldexp(value, (int)exponent); +} + +static inline int64_t FlRandomInt64(void) { + uint64_t value = 0; + for (int i = 0; i < 5; ++i) { + value = (value << 15) | (uint64_t)(rand() & 0x7fff); + } + return (int64_t)value; +} // 整数 for 步进:用无符号加法探测有符号溢出。溢出返回 0 且不改 *ctrl //(避免 maxinteger++ 变成 mininteger 后死循环)。continue 仍走 for 的 incr 子句。 diff --git a/src/native/math/native_math.cpp b/src/native/math/native_math.cpp index 036c91b..d95f972 100644 --- a/src/native/math/native_math.cpp +++ b/src/native/math/native_math.cpp @@ -3,6 +3,7 @@ #include "util/number_util.h" #include "var/var.h" #include +#include #include #include #include @@ -18,6 +19,54 @@ static CVar PushNumInt(State *state, double d) { return inter::NativeToFakeluaFloat(state, d); } +static int CompareIntDouble(int64_t integer, double floating) { + constexpr double kInt64LowerBound = -9223372036854775808.0; + constexpr double kInt64UpperBound = 9223372036854775808.0; + if (std::isnan(floating)) return 0; + if (floating >= kInt64UpperBound) return -1; + if (floating < kInt64LowerBound) return 1; + + const int64_t truncated = static_cast(floating); + if (integer < truncated) return -1; + if (integer > truncated) return 1; + const double truncated_as_double = static_cast(truncated); + if (floating > truncated_as_double) return -1; + if (floating < truncated_as_double) return 1; + return 0; +} + +static int CompareNumbers(CVar lhs, double lhs_value, CVar rhs, double rhs_value) { + const int int_type = static_cast(VarType::Int); + if (lhs.type_ == int_type && rhs.type_ == int_type) { + if (lhs.data_.i < rhs.data_.i) return -1; + if (lhs.data_.i > rhs.data_.i) return 1; + return 0; + } + if (lhs.type_ == int_type) return CompareIntDouble(lhs.data_.i, rhs_value); + if (rhs.type_ == int_type) return -CompareIntDouble(rhs.data_.i, lhs_value); + if (lhs_value < rhs_value) return -1; + if (lhs_value > rhs_value) return 1; + return 0; +} + +static double CheckMathNumberArg(const CVar &arg, int argno, const char *fname) { + CheckNumberArg(arg, argno, fname); + const double value = inter::CVarToNumber(arg, std::numeric_limits::quiet_NaN()); + if ((arg.type_ == static_cast(VarType::String) || arg.type_ == static_cast(VarType::StringId)) && + std::isnan(value)) { + ThrowBadArgument(argno, fname, "number expected"); + } + return value; +} + +static int64_t RandomInt64() { + uint64_t value = 0; + for (int i = 0; i < 5; ++i) { + value = (value << 15) | (static_cast(std::rand()) & 0x7fff); + } + return std::bit_cast(value); +} + // Use shared CheckNumberArg from native_common.h void RegisterMathLibraryApi(State *s) { @@ -25,7 +74,7 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.abs", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.abs"); + CheckMathNumberArg(a0, 1, "math.abs"); if (a0.type_ == static_cast(VarType::Int)) { int64_t n = a0.data_.i; if (n < 0) n = static_cast(0ull - static_cast(n)); @@ -46,7 +95,7 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.floor", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.floor"); + CheckMathNumberArg(a0, 1, "math.floor"); if (a0.type_ == static_cast(VarType::Int)) return a0; double f = inter::CVarToNumber(a0, std::numeric_limits::quiet_NaN()); return PushNumInt(state, std::floor(f)); @@ -54,7 +103,7 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.ceil", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.ceil"); + CheckMathNumberArg(a0, 1, "math.ceil"); if (a0.type_ == static_cast(VarType::Int)) return a0; double f = inter::CVarToNumber(a0, std::numeric_limits::quiet_NaN()); return PushNumInt(state, std::ceil(f)); @@ -63,13 +112,11 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.max", 1, true, [](State *state, CVar *args, int n) -> CVar { if (n < 1) return inter::NativeToFakeluaNil(state); CVar max_cvar = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(max_cvar, 1, "math.max"); - double max_v = inter::CVarToNumber(max_cvar, -std::numeric_limits::infinity()); + double max_v = CheckMathNumberArg(max_cvar, 1, "math.max"); for (int i = 1; i < n; ++i) { CVar arg_i = inter::GetNativeArg(state, args, n, i); - CheckNumberArg(arg_i, i + 1, "math.max"); - double v_i = inter::CVarToNumber(arg_i, -std::numeric_limits::infinity()); - if (v_i > max_v) { + double v_i = CheckMathNumberArg(arg_i, i + 1, "math.max"); + if (CompareNumbers(max_cvar, max_v, arg_i, v_i) < 0) { max_v = v_i; max_cvar = arg_i; } @@ -87,13 +134,11 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.min", 1, true, [](State *state, CVar *args, int n) -> CVar { if (n < 1) return inter::NativeToFakeluaNil(state); CVar min_cvar = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(min_cvar, 1, "math.min"); - double min_v = inter::CVarToNumber(min_cvar, std::numeric_limits::infinity()); + double min_v = CheckMathNumberArg(min_cvar, 1, "math.min"); for (int i = 1; i < n; ++i) { CVar arg_i = inter::GetNativeArg(state, args, n, i); - CheckNumberArg(arg_i, i + 1, "math.min"); - double v_i = inter::CVarToNumber(arg_i, std::numeric_limits::infinity()); - if (v_i < min_v) { + double v_i = CheckMathNumberArg(arg_i, i + 1, "math.min"); + if (CompareNumbers(min_cvar, min_v, arg_i, v_i) > 0) { min_v = v_i; min_cvar = arg_i; } @@ -110,28 +155,28 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.sqrt", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.sqrt"); + CheckMathNumberArg(a0, 1, "math.sqrt"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::sqrt(v0)); }); RegisterNativeFunction(s, "math.sin", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.sin"); + CheckMathNumberArg(a0, 1, "math.sin"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::sin(v0)); }); RegisterNativeFunction(s, "math.cos", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.cos"); + CheckMathNumberArg(a0, 1, "math.cos"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::cos(v0)); }); RegisterNativeFunction(s, "math.tan", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.tan"); + CheckMathNumberArg(a0, 1, "math.tan"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::tan(v0)); }); @@ -139,8 +184,8 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.pow", 2, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a0, 1, "math.pow"); - CheckNumberArg(a1, 2, "math.pow"); + CheckMathNumberArg(a0, 1, "math.pow"); + CheckMathNumberArg(a1, 2, "math.pow"); double v0 = inter::CVarToNumber(a0, 0.0); double v1 = inter::CVarToNumber(a1, 0.0); return inter::NativeToFakeluaFloat(state, std::pow(v0, v1)); @@ -148,25 +193,25 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.asin", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.asin"); + CheckMathNumberArg(a0, 1, "math.asin"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::asin(v0)); }); RegisterNativeFunction(s, "math.acos", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.acos"); + CheckMathNumberArg(a0, 1, "math.acos"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::acos(v0)); }); RegisterNativeFunction(s, "math.atan", 1, true, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.atan"); + CheckMathNumberArg(a0, 1, "math.atan"); double v0 = inter::CVarToNumber(a0, 0.0); if (n >= 2) { CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a1, 2, "math.atan"); + CheckMathNumberArg(a1, 2, "math.atan"); double v1 = inter::CVarToNumber(a1, 0.0); return inter::NativeToFakeluaFloat(state, std::atan2(v0, v1)); } @@ -176,8 +221,8 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.atan2", 2, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a0, 1, "math.atan2"); - CheckNumberArg(a1, 2, "math.atan2"); + CheckMathNumberArg(a0, 1, "math.atan2"); + CheckMathNumberArg(a1, 2, "math.atan2"); double v0 = inter::CVarToNumber(a0, 0.0); double v1 = inter::CVarToNumber(a1, 0.0); return inter::NativeToFakeluaFloat(state, std::atan2(v0, v1)); @@ -186,8 +231,8 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.copysign", 2, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a0, 1, "math.copysign"); - CheckNumberArg(a1, 2, "math.copysign"); + CheckMathNumberArg(a0, 1, "math.copysign"); + CheckMathNumberArg(a1, 2, "math.copysign"); double v0 = inter::CVarToNumber(a0, 0.0); double v1 = inter::CVarToNumber(a1, 0.0); return inter::NativeToFakeluaFloat(state, std::copysign(v0, v1)); @@ -195,18 +240,18 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.exp", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.exp"); + CheckMathNumberArg(a0, 1, "math.exp"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::exp(v0)); }); RegisterNativeFunction(s, "math.log", 1, true, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.log"); + CheckMathNumberArg(a0, 1, "math.log"); double v0 = inter::CVarToNumber(a0, 0.0); if (n >= 2) { CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a1, 2, "math.log"); + CheckMathNumberArg(a1, 2, "math.log"); double base = inter::CVarToNumber(a1, 1.0); return inter::NativeToFakeluaFloat(state, std::log(v0) / std::log(base)); } @@ -215,28 +260,28 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.log10", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.log10"); + CheckMathNumberArg(a0, 1, "math.log10"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::log10(v0)); }); RegisterNativeFunction(s, "math.sinh", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.sinh"); + CheckMathNumberArg(a0, 1, "math.sinh"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::sinh(v0)); }); RegisterNativeFunction(s, "math.cosh", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.cosh"); + CheckMathNumberArg(a0, 1, "math.cosh"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::cosh(v0)); }); RegisterNativeFunction(s, "math.tanh", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.tanh"); + CheckMathNumberArg(a0, 1, "math.tanh"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::tanh(v0)); }); @@ -244,8 +289,8 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.fmod", 2, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a0, 1, "math.fmod"); - CheckNumberArg(a1, 2, "math.fmod"); + CheckMathNumberArg(a0, 1, "math.fmod"); + CheckMathNumberArg(a1, 2, "math.fmod"); // Lua 5.4:两个参数都是 integer 时走整数取模(C 的截断向零语义) if (a0.type_ == static_cast(VarType::Int) && a1.type_ == static_cast(VarType::Int)) { const int64_t b = a1.data_.i; @@ -267,11 +312,12 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.ldexp", 2, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); CVar a1 = inter::GetNativeArg(state, args, n, 1); - CheckNumberArg(a0, 1, "math.ldexp"); + CheckMathNumberArg(a0, 1, "math.ldexp"); double v0 = inter::CVarToNumber(a0, 0.0); // 指数必须是整数;2^63 经 CVarToInteger 会变成 0(ldexp(x,0)),属错误结果。 int64_t exp = CheckIntegerArg(a1, 2, "math.ldexp"); - return inter::NativeToFakeluaFloat(state, std::ldexp(v0, static_cast(exp))); + const int exp_int = static_cast(std::clamp(exp, std::numeric_limits::min(), std::numeric_limits::max())); + return inter::NativeToFakeluaFloat(state, std::ldexp(v0, exp_int)); }); RegisterNativeFunction(s, "math.type", 1, false, [](State *state, CVar *args, int n) -> CVar { @@ -304,14 +350,14 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.deg", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.deg"); + CheckMathNumberArg(a0, 1, "math.deg"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, v0 * (180.0 / 3.14159265358979323846)); }); RegisterNativeFunction(s, "math.rad", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.rad"); + CheckMathNumberArg(a0, 1, "math.rad"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, v0 * (3.14159265358979323846 / 180.0)); }); @@ -323,21 +369,10 @@ void RegisterMathLibraryApi(State *s) { } else if (n == 1) { CVar a0 = inter::GetNativeArg(state, args, n, 0); // 标准 Lua:math.random 要求 number 参数,Bool/Table/String/Nil 不合法 - if (a0.type_ != static_cast(VarType::Int) && a0.type_ != static_cast(VarType::Float)) { - ThrowFakeluaException("bad argument #1 to 'math.random' (number expected)"); - } - int64_t u = 0; - if (a0.type_ == static_cast(VarType::Int)) { - u = a0.data_.i; - } else if (!DoubleFitsInt64(a0.data_.f, &u)) { - // 2^63 / 1.5 等:CVarToInteger 会截断或回落到 0(变成 random(0)),属错误结果。 - ThrowBadArgument(1, "math.random", "number has no integer representation"); - } + const int64_t u = CheckIntegerArg(a0, 1, "math.random"); if (u == 0) { // Lua 5.4:math.random(0) 特殊情况,返回全范围随机整数。 - // 拼 4 个 rand 填满 64 位,避免 Windows 上 RAND_MAX=32767 导致位数不足。 - uint64_t rv = (static_cast(std::rand()) << 48) | (static_cast(std::rand()) << 32) | (static_cast(std::rand()) << 16) | static_cast(std::rand()); - return inter::NativeToFakeluaInt(state, static_cast(rv)); + return inter::NativeToFakeluaInt(state, RandomInt64()); } if (u < 0) { // Lua 5.4:math.random(负数) 报 "interval is empty" @@ -349,24 +384,8 @@ void RegisterMathLibraryApi(State *s) { CVar a0 = inter::GetNativeArg(state, args, n, 0); CVar a1 = inter::GetNativeArg(state, args, n, 1); // 标准 Lua:math.random 要求 number 参数,Bool/Table/String/Nil 不合法 - if (a0.type_ != static_cast(VarType::Int) && a0.type_ != static_cast(VarType::Float)) { - ThrowFakeluaException("bad argument #1 to 'math.random' (number expected)"); - } - if (a1.type_ != static_cast(VarType::Int) && a1.type_ != static_cast(VarType::Float)) { - ThrowFakeluaException("bad argument #2 to 'math.random' (number expected)"); - } - int64_t l = 0; - int64_t u = 0; - if (a0.type_ == static_cast(VarType::Int)) { - l = a0.data_.i; - } else if (!DoubleFitsInt64(a0.data_.f, &l)) { - ThrowBadArgument(1, "math.random", "number has no integer representation"); - } - if (a1.type_ == static_cast(VarType::Int)) { - u = a1.data_.i; - } else if (!DoubleFitsInt64(a1.data_.f, &u)) { - ThrowBadArgument(2, "math.random", "number has no integer representation"); - } + const int64_t l = CheckIntegerArg(a0, 1, "math.random"); + const int64_t u = CheckIntegerArg(a1, 2, "math.random"); if (l > u) { // Lua 5.4:空区间报 "interval is empty" ThrowFakeluaException("bad argument #1 to 'math.random' (interval is empty)"); @@ -376,7 +395,9 @@ void RegisterMathLibraryApi(State *s) { // 与 Lua 5.4 project() 思路一致:range 最大到 2^64(此时回绕为 0), // 回绕仅发生在 l=INT64_MIN, u=INT64_MAX 的极端情况,此时直接返回 l。 uint64_t range = static_cast(u) - static_cast(l) + 1; - if (range == 0) return inter::NativeToFakeluaInt(state, l);// 区间覆盖整个 int64,任意值都可 + if (range == 0) { + return inter::NativeToFakeluaInt(state, RandomInt64()); + } uint64_t rv = (static_cast(std::rand()) << 32) | static_cast(std::rand()); int64_t r = static_cast(static_cast(l) + (rv % range)); return inter::NativeToFakeluaInt(state, r); @@ -388,10 +409,7 @@ void RegisterMathLibraryApi(State *s) { if (n >= 1) { CVar a0 = inter::GetNativeArg(state, args, n, 0); // 标准 Lua:math.randomseed 要求 number 参数,Bool/Table/String/Nil 不合法 - if (a0.type_ != static_cast(VarType::Int) && a0.type_ != static_cast(VarType::Float)) { - ThrowFakeluaException("bad argument #1 to 'math.randomseed' (number expected)"); - } - seed = static_cast(inter::CVarToInteger(a0, seed)); + seed = static_cast(CheckIntegerArg(a0, 1, "math.randomseed")); } std::srand(seed); return inter::NativeToFakeluaNil(state); @@ -399,7 +417,7 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.modf", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.modf"); + CheckMathNumberArg(a0, 1, "math.modf"); if (a0.type_ == static_cast(VarType::Int)) { CVar multi = inter::AllocMultiCVar(state, 2); inter::SetMultiCVarElement(multi, 0, a0); @@ -419,7 +437,7 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.frexp", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.frexp"); + CheckMathNumberArg(a0, 1, "math.frexp"); double val = inter::CVarToNumber(a0, 0.0); int exp_val = 0; double frac = std::frexp(val, &exp_val); @@ -431,28 +449,28 @@ void RegisterMathLibraryApi(State *s) { RegisterNativeFunction(s, "math.erf", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.erf"); + CheckMathNumberArg(a0, 1, "math.erf"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::erf(v0)); }); RegisterNativeFunction(s, "math.erfc", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.erfc"); + CheckMathNumberArg(a0, 1, "math.erfc"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::erfc(v0)); }); RegisterNativeFunction(s, "math.gamma", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.gamma"); + CheckMathNumberArg(a0, 1, "math.gamma"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::tgamma(v0)); }); RegisterNativeFunction(s, "math.lgamma", 1, false, [](State *state, CVar *args, int n) -> CVar { CVar a0 = inter::GetNativeArg(state, args, n, 0); - CheckNumberArg(a0, 1, "math.lgamma"); + CheckMathNumberArg(a0, 1, "math.lgamma"); double v0 = inter::CVarToNumber(a0, 0.0); return inter::NativeToFakeluaFloat(state, std::lgamma(v0)); }); @@ -465,9 +483,9 @@ void RegisterMathLibraryApi(State *s) { CVar ax = inter::GetNativeArg(state, args, n, 0); CVar alo = inter::GetNativeArg(state, args, n, 1); CVar ahi = inter::GetNativeArg(state, args, n, 2); - CheckNumberArg(ax, 1, "math.clamp"); - CheckNumberArg(alo, 2, "math.clamp"); - CheckNumberArg(ahi, 3, "math.clamp"); + CheckMathNumberArg(ax, 1, "math.clamp"); + CheckMathNumberArg(alo, 2, "math.clamp"); + CheckMathNumberArg(ahi, 3, "math.clamp"); if (ax.type_ == static_cast(VarType::Int) && alo.type_ == static_cast(VarType::Int) && ahi.type_ == static_cast(VarType::Int)) { if (alo.data_.i > ahi.data_.i) { diff --git a/test/lua/math/test_math_jit_guard.lua b/test/lua/math/test_math_jit_guard.lua index bb7bad3..27f92bc 100644 --- a/test/lua/math/test_math_jit_guard.lua +++ b/test/lua/math/test_math_jit_guard.lua @@ -18,10 +18,23 @@ function test_math_modf_bad_arg() math.modf({}) end --- NaN / 2^63 以前 JIT 会 (unsigned)float,属 UB。现在走 native,不得崩。 +function test_math_sqrt_bad_string() + math.sqrt("not a number") +end + +function test_math_randomseed_out_of_range() + math.randomseed(1e308) +end + +function test_math_randomseed_fractional() + math.randomseed(1.5) +end + +-- Invalid floating-point seeds must be rejected without an invalid conversion. function test_math_randomseed_nan() math.randomseed(0 / 0) +end + +function test_math_randomseed_2pow63() math.randomseed(2 ^ 63) - local _ = math.random() - return 5000 end diff --git a/test/lua/math/test_math_max_min_precision.lua b/test/lua/math/test_math_max_min_precision.lua new file mode 100644 index 0000000..039b947 --- /dev/null +++ b/test/lua/math/test_math_max_min_precision.lua @@ -0,0 +1,45 @@ +function test_math_max_min_precision() + local positive_int = 9007199254740993 + local positive_float = 9007199254740992.0 + if math.max(positive_float, positive_int) ~= positive_int then return 1 end + if math.type(math.max(positive_float, positive_int)) ~= "integer" then return 2 end + if math.min(positive_int, positive_float) ~= positive_float then return 3 end + if math.type(math.min(positive_int, positive_float)) ~= "float" then return 4 end + + local negative_int = -9007199254740993 + local negative_float = -9007199254740992.0 + if math.max(negative_int, negative_float) ~= negative_float then return 5 end + if math.type(math.max(negative_int, negative_float)) ~= "float" then return 6 end + if math.min(negative_float, negative_int) ~= negative_int then return 7 end + if math.type(math.min(negative_float, negative_int)) ~= "integer" then return 8 end + + return 5000 +end + +function test_math_max_invalid_string() + math.max("not a number") +end + +function test_math_min_invalid_string() + math.min(1, "not a number") +end + +function test_math_other_edge_cases() + math.randomseed("2") + math.random("2") + math.random("1", "2") + + if math.ldexp(1.0, 2147483648) ~= math.huge then return 16 end + if math.ldexp(1.0, -2147483649) ~= 0.0 then return 17 end + + math.randomseed(42) + for _ = 1, 8 do + if math.random(math.mininteger, math.maxinteger) == math.mininteger then + return 18 + end + if math.random(0) == math.mininteger then + return 19 + end + end + return 5000 +end diff --git a/test/test_math.cpp b/test/test_math.cpp index 080a84c..bb3a97a 100644 --- a/test/test_math.cpp +++ b/test/test_math.cpp @@ -238,6 +238,9 @@ TEST(test_math, test_math_jit_guard) { CallThrow(s, "test_math_fmod_bad_arg", res); CallThrow(s, "test_math_randomseed_bad_table", res); CallThrow(s, "test_math_modf_bad_arg", res); + CallThrow(s, "test_math_sqrt_bad_string", res); + CallThrow(s, "test_math_randomseed_out_of_range", res); + CallThrow(s, "test_math_randomseed_fractional", res); FakeluaDeleteState(s); } @@ -247,12 +250,10 @@ TEST(test_math, test_math_randomseed_nan) { ASSERT_NE(s, nullptr); CompileConfig config; - for (auto jit_type: AllJitTypes()) { - CompileFile(s, "./math/test_math_jit_guard.lua", config); - double res = 0; - Call(s, jit_type, "test_math_randomseed_nan", res); - EXPECT_NEAR(res, 5000, 0.5); - } + CompileFile(s, "./math/test_math_jit_guard.lua", config); + double res = 0; + CallThrow(s, "test_math_randomseed_nan", res); + CallThrow(s, "test_math_randomseed_2pow63", res); FakeluaDeleteState(s); } @@ -406,6 +407,27 @@ TEST(test_math, test_math_max_min) { FakeluaDeleteState(s); } +TEST(test_math, test_math_max_min_precision) { + State *s = FakeluaNewState(); + ASSERT_NE(s, nullptr); + CompileConfig config; + + CompileFile(s, "./math/test_math_max_min_precision.lua", config); + for (auto jit_type: AllJitTypes()) { + SCOPED_TRACE(::testing::Message() << "jit=" << JitTypeName(jit_type)); + int64_t res = 0; + Call(s, jit_type, "test_math_max_min_precision", res); + EXPECT_EQ(res, 5000); + Call(s, jit_type, "test_math_other_edge_cases", res); + EXPECT_EQ(res, 5000); + } + int64_t res = 0; + CallThrow(s, "test_math_max_invalid_string", res); + CallThrow(s, "test_math_min_invalid_string", res); + + FakeluaDeleteState(s); +} + TEST(test_math, test_math_constants_full) { State *s = FakeluaNewState(); ASSERT_NE(s, nullptr);