Files

575 lines
23 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::sync::Arc;
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
vm.run(&Arc::new(module))
}
#[test]
fn number_is_nan_with_nan() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.isNaN(NaN)").unwrap();
assert!(result.as_bool());
}
#[test]
fn number_is_nan_with_number() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.isNaN(42)").unwrap();
assert!(!result.as_bool());
}
#[test]
fn number_is_finite_with_number() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.isFinite(42)").unwrap();
assert!(result.as_bool());
}
#[test]
fn number_is_finite_with_infinity() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.isFinite(1/0)").unwrap();
assert!(!result.as_bool());
}
#[test]
fn parse_int_decimal() {
let mut vm = Vm::new();
let result = eval(&mut vm, "parseInt('42')").unwrap();
assert_eq!(result.as_int(), 42);
}
#[test]
fn parse_int_hex() {
let mut vm = Vm::new();
let result = eval(&mut vm, "parseInt('0xFF')").unwrap();
assert_eq!(result.as_int(), 255);
}
#[test]
fn parse_float_decimal() {
let mut vm = Vm::new();
let result = eval(&mut vm, "parseFloat('3.14')").unwrap();
#[allow(clippy::approx_constant)]
let expected = 3.14;
assert!((result.as_double() - expected).abs() < 0.001);
}
#[test]
fn parse_float_invalid() {
let mut vm = Vm::new();
let result = eval(&mut vm, "parseFloat('abc')").unwrap();
assert!(result.as_double().is_nan());
}
#[test]
fn parse_int_prefix_and_radix_semantics() {
let mut vm = Vm::new();
let cases = [
// 前缀解析:遇非法字符停止,取最长合法数字前缀。
("parseInt('123abc') === 123", true),
// +/- 符号前缀。
("parseInt('+42') === 42", true),
("parseInt('-42') === -42", true),
// radix 显式时不触发 0x;缺省/0/16 时触发。
("parseInt('0x10', 10) === 0", true),
("parseInt('0x10') === 16", true),
("parseInt('0x10', 0) === 16", true),
("parseInt('0x10', 16) === 16", true),
("parseInt('+0x10') === 16", true),
("parseInt('0XFF') === 255", true),
// 超 i32 用 f64 近似(允许舍入),i32 域内保持精确。
("parseInt('99999999999999999999') === 1e20", true),
("parseInt('9007199254740993') === 9007199254740992", true),
("parseInt('2147483647') === 2147483647", true),
("parseInt('2147483648') === 2147483648", true),
// radix 有效字符过滤与空白处理。
("parseInt('10', 2) === 2", true),
("parseInt('1a', 2) === 1", true),
("parseInt(' -42 ') === -42", true),
// 负零保留符号。
("1 / parseInt('-0') === -Infinity", true),
// 无有效数字 / radix 越界 / 超大 radix 经 ToInt32 回绕。
("isNaN(parseInt(''))", true),
("isNaN(parseInt('0x'))", true),
("isNaN(parseInt('0xG'))", true),
("isNaN(parseInt('z'))", true),
("isNaN(parseInt('10', 1))", true),
("isNaN(parseInt('10', 37))", true),
("isNaN(parseInt('z', 2 ** 40))", true),
];
for (src, expected) in cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(result.as_bool(), expected, "for {}", src);
}
}
#[test]
fn parse_float_prefix_semantics() {
let mut vm = Vm::new();
let cases = [
// 前缀解析:取最长合法 StrDecimalLiteral。
("parseFloat('3.14abc') === 3.14", true),
("parseFloat('1.2.3') === 1.2", true),
("parseFloat('1e5') === 100000", true),
("parseFloat('1e+5') === 100000", true),
("parseFloat('.5') === 0.5", true),
("parseFloat(' 3.14 ') === 3.14", true),
// Infinity 大小写敏感,可带符号与后缀。
("parseFloat('Infinity') === Infinity", true),
("parseFloat('+Infinity') === Infinity", true),
("parseFloat('-Infinity') === -Infinity", true),
("parseFloat('Infinityx') === Infinity", true),
("isNaN(parseFloat('-infinity'))", true),
// 十六进制不解析,十进制前缀在 'x' 处停止;溢出归 Infinity。
("parseFloat('0x10') === 0", true),
("parseFloat('1e400') === Infinity", true),
("parseFloat('-1e400') === -Infinity", true),
// 无合法前缀。
("isNaN(parseFloat(''))", true),
("isNaN(parseFloat('.'))", true),
("isNaN(parseFloat('.e5'))", true),
("isNaN(parseFloat('abc'))", true),
// 指数后无数字:'e' 不构成 ExponentPart,最长合法前缀为整数部分。
("parseFloat('1e') === 1", true),
("parseFloat('1e+') === 1", true),
];
for (src, expected) in cases {
let result = eval(&mut vm, src).unwrap();
assert_eq!(result.as_bool(), expected, "for {}", src);
}
}
#[test]
fn parse_parse_int_object_arg_tostring() {
let mut vm = Vm::new();
// 对象参数经 ToString 完整转换:取 toString 结果再解析。
let result = eval(&mut vm, "parseInt({toString: () => '42'})").unwrap();
assert_eq!(result.as_int(), 42);
#[allow(clippy::approx_constant)]
let expected = 3.14;
let result = eval(&mut vm, "parseFloat({toString: () => '3.14abc'})").unwrap();
assert!((result.as_double() - expected).abs() < 0.001);
// Symbol 参数按规范抛 TypeError。
let err = eval(&mut vm, "parseInt(Symbol())").unwrap_err();
assert!(err.contains("TypeError"), "got: {}", err);
let err = eval(&mut vm, "parseFloat(Symbol('x'))").unwrap_err();
assert!(err.contains("TypeError"), "got: {}", err);
// 对象 toString 抛出的原始异常原样传播(保留异常对象)。
let err = eval(&mut vm, "try { parseInt({toString(){ throw 42 }}) } catch (e) { e }").unwrap();
assert_eq!(err.as_int(), 42);
}
#[test]
fn number_constructor() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number('42')").unwrap();
assert_eq!(result.as_int(), 42);
}
#[test]
fn to_string_of_number() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 42; n.toString()").unwrap();
let s = vm.lookup_str(result).unwrap_or_default();
assert_eq!(s, "42");
}
#[test]
fn to_fixed_of_number() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 3.14159; n.toFixed(2)").unwrap();
let s = vm.lookup_str(result).unwrap_or_default();
assert_eq!(s, "3.14");
}
#[test]
fn to_string_scientific_boundaries() {
let mut vm = Vm::new();
let cases = [
("(1e21).toString()", "1e+21"),
("(1e20).toString()", "100000000000000000000"),
("(123.45).toString()", "123.45"),
("(0.000001).toString()", "0.000001"),
("(0.0000001).toString()", "1e-7"),
("(1e15).toString()", "1000000000000000"),
("(0.1+0.2).toString()", "0.30000000000000004"),
("String(1e21)", "1e+21"),
("(\"\" + 1e21)", "1e+21"),
("(-1e21).toString()", "-1e+21"),
("JSON.stringify(1e21)", "1e+21"),
("JSON.stringify(1e20)", "100000000000000000000"),
// 整数 double 快路径(2^53 内直写)。
("(42.0).toString()", "42"),
("(1e10).toString()", "10000000000"),
("String(-42.0)", "-42"),
("(-0).toString()", "0"),
("JSON.stringify(1.5)", "1.5"),
("JSON.stringify(-0)", "0"),
// 2^53 边界与 1e22:超出快路径范围仍输出规范形态。
("Math.pow(2,53).toString()", "9007199254740992"),
("Math.pow(2,63).toString()", "9223372036854776000"),
("(1e22).toString()", "1e+22"),
("(1.5e20).toString()", "150000000000000000000"),
("(9999999999999999).toString()", "10000000000000000"),
];
for (src, expected) in cases {
let result = eval(&mut vm, src).unwrap();
let s = vm.lookup_str(result).unwrap_or_default();
assert_eq!(s, expected, "for {}", src);
}
}
#[test]
fn to_string_radix_and_range_error() {
let mut vm = Vm::new();
let result = eval(&mut vm, "(255).toString(16)").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "ff");
let result = eval(&mut vm, "(2).toString(2)").unwrap();
assert_eq!(vm.lookup_str(result).unwrap_or_default(), "10");
let err = eval(&mut vm, "(10).toString(1)").unwrap_err();
assert!(err.contains("RangeError"), "got: {}", err);
let err = eval(&mut vm, "(10).toString(37)").unwrap_err();
assert!(err.contains("RangeError"), "got: {}", err);
}
#[test]
fn number_constructor_string_rules() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number('0xa')").unwrap();
assert_eq!(result.as_int(), 10);
let result = eval(&mut vm, "Number('')").unwrap();
assert_eq!(result.as_int(), 0);
let result = eval(&mut vm, "Number('INFINITY')").unwrap();
assert!(result.as_double().is_nan());
}
#[test]
fn number_is_integer_true() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.isInteger(42)").unwrap();
assert!(result.as_bool());
}
#[test]
fn number_is_integer_false() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.isInteger(42.5)").unwrap();
assert!(!result.as_bool());
}
#[test]
fn number_epsilon_is_positive() {
let mut vm = Vm::new();
let result = eval(&mut vm, "Number.EPSILON > 0").unwrap();
assert!(result.as_bool());
}
#[test]
fn number_to_exponential() {
let mut vm = Vm::new();
let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap();
let s = vm.lookup_str(result).unwrap_or_default();
// 指数恒带符号(规范科学式形态)。
assert_eq!(s, "1.23e+2");
}
#[test]
fn number_static_constants_remain_bound() {
let mut vm = Vm::new();
assert_eq!(eval(&mut vm, "Number.MAX_SAFE_INTEGER").unwrap().as_double(), 9007199254740991f64);
assert!(eval(&mut vm, "Number.POSITIVE_INFINITY > 0").unwrap().as_bool());
}
#[test]
fn boxed_number_valueof_preserves_int_and_float() {
let mut vm = Vm::new();
let i = eval(&mut vm, "new Number(5).valueOf()").unwrap();
assert!(i.is_int());
assert_eq!(i.as_int(), 5);
let f = eval(&mut vm, "new Number(1.5).valueOf()").unwrap();
assert!(f.is_double());
assert_eq!(f.as_double(), 1.5);
let s = eval(&mut vm, "new Number('7').valueOf()").unwrap();
assert_eq!(s.as_int(), 7);
}
#[test]
fn boxed_number_is_object_and_call_stays_primitive() {
let mut vm = Vm::new();
let ty = eval(&mut vm, "typeof new Number(5)").unwrap();
assert_eq!(vm.lookup_str(ty).unwrap(), "object");
let n = eval(&mut vm, "Number('7')").unwrap();
assert_eq!(n.as_int(), 7);
}
fn str_of(vm: &mut Vm, source: &str) -> String {
let r = eval(vm, source).unwrap();
vm.lookup_str(r).unwrap_or_default()
}
#[test]
fn number_proto_methods_this_number_value() {
let mut vm = Vm::new();
// Number 原始值与 Number 对象均合法 this。
assert_eq!(str_of(&mut vm, "(new Number(7)).toFixed(1)"), "7.0");
assert_eq!(str_of(&mut vm, "(new Number(7)).toPrecision(2)"), "7.0");
assert_eq!(str_of(&mut vm, "(new Number(7)).toExponential(1)"), "7.0e+0");
assert_eq!(str_of(&mut vm, "(new Number(255)).toString(16)"), "ff");
// Number.prototype 本体是 [[NumberData]] = +0 的 Number 对象。
assert_eq!(str_of(&mut vm, "Number.prototype.toFixed(1)"), "0.0");
assert_eq!(str_of(&mut vm, "Number.prototype.toExponential(0)"), "0e+0");
// 其余 this 一律 TypeError(四方法非通用)。
for method in ["toString", "toFixed", "toExponential", "toPrecision"] {
let src = format!("Number.prototype.{}.call({{}})", method);
let err = eval(&mut vm, &src).unwrap_err();
assert!(err.contains("TypeError"), "for {}: {}", method, err);
}
let err = eval(&mut vm, "Number.prototype.toFixed.call(new String('1'))").unwrap_err();
assert!(err.contains("TypeError"), "got: {}", err);
let err = eval(&mut vm, "Number.prototype.toPrecision.call(null)").unwrap_err();
assert!(err.contains("TypeError"), "got: {}", err);
}
#[test]
fn number_methods_argument_coercion_and_ordering() {
let mut vm = Vm::new();
// 参数走完整 ToNumber:Symbol/BigInt 抛 TypeError。
for method in ["toFixed", "toExponential", "toPrecision"] {
let err = eval(&mut vm, &format!("(0).{}(0n)", method)).unwrap_err();
assert!(err.contains("TypeError"), "bigint for {}: {}", method, err);
let err = eval(&mut vm, &format!("(0).{}(Symbol())", method)).unwrap_err();
assert!(err.contains("TypeError"), "symbol for {}: {}", method, err);
}
// 参数 valueOf 抛出的原始异常原样传播(保留异常值)。
let v = eval(&mut vm, "try { (0).toFixed({valueOf(){ throw 42 }}) } catch (e) { e }").unwrap();
assert_eq!(v.as_int(), 42);
// toFixed:范围检查先于 NaN 短路。
let err = eval(&mut vm, "NaN.toFixed(Infinity)").unwrap_err();
assert!(err.contains("RangeError"), "got: {}", err);
assert_eq!(str_of(&mut vm, "NaN.toFixed(0)"), "NaN");
// toPrecision:NaN/±∞ 专名先于范围检查。
assert_eq!(str_of(&mut vm, "NaN.toPrecision(1000)"), "NaN");
assert_eq!(str_of(&mut vm, "(Infinity).toPrecision(1000)"), "Infinity");
assert_eq!(str_of(&mut vm, "(-Infinity).toPrecision(1000)"), "-Infinity");
let err = eval(&mut vm, "(10).toPrecision(0)").unwrap_err();
assert!(err.contains("RangeError"), "got: {}", err);
// toExponential:NaN/±∞ 专名先于范围检查。
assert_eq!(str_of(&mut vm, "NaN.toExponential(101)"), "NaN");
assert_eq!(str_of(&mut vm, "(Infinity).toExponential(101)"), "Infinity");
let err = eval(&mut vm, "(3).toExponential(101)").unwrap_err();
assert!(err.contains("RangeError"), "got: {}", err);
let err = eval(&mut vm, "(3).toExponential(-1)").unwrap_err();
assert!(err.contains("RangeError"), "got: {}", err);
// toExponential 的 undefined 走最短式,非按 0 位处理。
assert_eq!(str_of(&mut vm, "(123.456).toExponential(undefined)"), "1.23456e+2");
}
#[test]
fn number_methods_default_precision_and_boundaries() {
let mut vm = Vm::new();
// toPrecision 缺省 precision = ToString(x)。
assert_eq!(str_of(&mut vm, "(42).toPrecision()"), "42");
assert_eq!(str_of(&mut vm, "(123.456).toPrecision(undefined)"), "123.456");
assert_eq!(str_of(&mut vm, "NaN.toPrecision()"), "NaN");
assert_eq!(str_of(&mut vm, "Number.prototype.toPrecision()"), "0");
// toFixed 的 x ≥ 1e21 退化为 String 科学式。
assert_eq!(str_of(&mut vm, "(1e21).toFixed()"), "1e+21");
assert_eq!(str_of(&mut vm, "(-1e21).toFixed(0)"), "-1e+21");
assert_eq!(str_of(&mut vm, "(new Number(1e21)).toFixed()"), "1e+21");
// -0 三面:构造、toFixed、toExponential。
assert_eq!(str_of(&mut vm, "String(Object.is(Number(-0), -0))"), "true");
assert_eq!(str_of(&mut vm, "String(Object.is(new Number(-0).valueOf(), -0))"), "true");
assert_eq!(str_of(&mut vm, "(-0).toFixed(0)"), "0");
assert_eq!(str_of(&mut vm, "(-0).toExponential(0)"), "0e+0");
assert_eq!(str_of(&mut vm, "(-0).toPrecision(2)"), "0.0");
// 指数恒带符号。
assert_eq!(str_of(&mut vm, "(3).toExponential(0)"), "3e+0");
assert_eq!(str_of(&mut vm, "(0).toExponential(1)"), "0.0e+0");
assert_eq!(str_of(&mut vm, "(10).toPrecision(1)"), "1e+1");
assert_eq!(str_of(&mut vm, "(1.1e-32).toExponential()"), "1.1e-32");
}
#[test]
fn number_isnan_isfinite_strict_type() {
let mut vm = Vm::new();
// 非 Number 类型(含字符串/对象)一律 false,不强转。
assert_eq!(str_of(&mut vm, "String(Number.isFinite('1'))"), "false");
assert_eq!(str_of(&mut vm, "String(Number.isNaN('NaN'))"), "false");
assert_eq!(str_of(&mut vm, "String(Number.isFinite(new Number(42)))"), "false");
assert_eq!(str_of(&mut vm, "String(Number.isFinite(42))"), "true");
assert_eq!(str_of(&mut vm, "String(Number.isNaN(NaN))"), "true");
assert_eq!(str_of(&mut vm, "String(Number.isFinite(Infinity))"), "false");
}
#[test]
fn number_proto_tag_and_metadata() {
let mut vm = Vm::new();
// Number.prototype 本体 tag 为 Number(品牌表据 type_tag 命中)。
assert_eq!(str_of(&mut vm, "Object.prototype.toString.call(Number.prototype)"), "[object Number]");
// parseInt/parseFloat 与全局同名函数是同一函数对象。
assert_eq!(str_of(&mut vm, "String(Number.parseInt === parseInt)"), "true");
assert_eq!(str_of(&mut vm, "String(Number.parseFloat === parseFloat)"), "true");
// toLocaleString 为 own 属性,行为同 toString(locale 参数忽略)。
assert_eq!(str_of(&mut vm, "String(Number.prototype.hasOwnProperty('toLocaleString'))"), "true");
assert_eq!(str_of(&mut vm, "(1.5).toLocaleString()"), "1.5");
// 四方法 length 为 1。
assert_eq!(str_of(&mut vm, "String(Number.prototype.toString.length)"), "1");
assert_eq!(str_of(&mut vm, "String(Number.prototype.toFixed.length)"), "1");
assert_eq!(str_of(&mut vm, "String(Number.prototype.toExponential.length)"), "1");
assert_eq!(str_of(&mut vm, "String(Number.prototype.toPrecision.length)"), "1");
}
/// full_reset 脏重建:number 家族重建后 Number.prototype 仍带 Number 对象 tag
/// 与 +0 包值(漏此分支则 tag 翻回 "Object")。
#[test]
fn number_proto_tag_survives_dirty_rebuild() {
let mut vm = Vm::new();
// 属性写使 number 家族置脏,触发选择性重建路径。
eval(&mut vm, "Number.prototype.marker = 1").unwrap();
vm.full_reset();
assert_eq!(str_of(&mut vm, "Object.prototype.toString.call(Number.prototype)"), "[object Number]");
assert_eq!(str_of(&mut vm, "Number.prototype.toFixed(1)"), "0.0");
assert_eq!(str_of(&mut vm, "String(Number.parseInt === parseInt)"), "true");
}
// 全表值对照 node v20.19.2 逐位核定(half-up 对 half-even 的分歧点、
// 进位传播、位数增长、精确展开面、零与 -0 三面)。
#[test]
fn number_format_tie_half_up_and_carry() {
let mut vm = Vm::new();
let cases = [
// tie 取较大者(away-from-zero),负数同形。
("(2.5).toExponential(0)", "3e+0"),
("(2.4).toExponential(0)", "2e+0"),
("(-2.5).toExponential(0)", "-3e+0"),
("(-0.5).toExponential(0)", "-5e-1"),
("(0.5).toFixed(0)", "1"),
("(0.4).toFixed(0)", "0"),
("(2.5).toFixed(0)", "3"),
("(-0.5).toFixed(0)", "-1"),
("(2.675).toFixed(2)", "2.67"),
// 精确展开低于 tie 点时不得进位(仅看最短形会误进位)。
("(1.15).toFixed(1)", "1.1"),
("(1.005).toFixed(2)", "1.00"),
("(1.005).toFixed(1)", "1.0"),
("(0.05).toFixed(1)", "0.1"),
("(0.145).toFixed(2)", "0.14"),
("(1.2345).toFixed(3)", "1.234"),
("(1.0005).toFixed(3)", "1.000"),
("(0.615).toFixed(2)", "0.61"),
// 进位传播与位数增长(9.99→10.0 型)。
("(9.999).toFixed(1)", "10.0"),
("(9.999).toFixed(0)", "10"),
("(-9.999).toFixed(1)", "-10.0"),
("(0.9999).toExponential(0)", "1e+0"),
("(-0.9999).toExponential(0)", "-1e+0"),
("(9.9999999999999999).toFixed(0)", "10"),
("(999).toPrecision(2)", "1.0e+3"),
("(-999).toPrecision(2)", "-1.0e+3"),
("(99.99).toPrecision(3)", "100"),
("(999999).toPrecision(2)", "1.0e+6"),
("(25).toExponential(0)", "3e+1"),
("(12345).toExponential(3)", "1.235e+4"),
// 精确整数展开(位数超最短形)。
("(1000000000000000128).toFixed(0)", "1000000000000000128"),
("(9007199254740993).toFixed(0)", "9007199254740992"),
("(9.9e20).toFixed(0)", "990000000000000000000"),
// 进位后 e 决定定点/指数分界。
("(1.23456).toPrecision(4)", "1.235"),
("(123456).toPrecision(3)", "1.23e+5"),
("(1e-6).toPrecision(2)", "0.0000010"),
("(1e-7).toPrecision(2)", "1.0e-7"),
("(0.5).toPrecision(1)", "0.5"),
("(0.05).toPrecision(2)", "0.050"),
("(10).toPrecision(1)", "1e+1"),
// 零与 -0 三面(-0 落正分支)。
("(0).toFixed(3)", "0.000"),
("(0).toExponential(2)", "0.00e+0"),
("(0).toPrecision(2)", "0.0"),
("(-0).toFixed(3)", "0.000"),
("(-0).toExponential(2)", "0.00e+0"),
("(-0).toPrecision(2)", "0.0"),
// x ≥ 1e21 走科学式 String(f64 已达 1e21)。
("(1e21-1).toFixed(0)", "1e+21"),
("(-1e21).toFixed(0)", "-1e+21"),
("(1234567890123456789012345678901234567890).toFixed(0)", "1.2345678901234568e+39"),
// 长小数面(展开终止后按 0 补齐)。
("(3.141592653589793).toPrecision(17)", "3.1415926535897931"),
(
"(3.141592653589793).toFixed(100)",
"3.1415926535897931159979634685441851615905761718750000000000000000000000000000000000000000000000000000",
),
// 次正规数与超大指数面。
("(1e308).toPrecision(2)", "1.0e+308"),
("(1.5e308).toExponential(1)", "1.5e+308"),
("(1e-323).toExponential(2)", "9.88e-324"),
("(5e-324).toExponential(0)", "5e-324"),
("(4.9e-324).toFixed(1)", "0.0"),
];
for (src, expected) in cases {
assert_eq!(str_of(&mut vm, src), expected, "for {}", src);
}
}
#[test]
fn number_format_default_shortest_digits() {
let mut vm = Vm::new();
// 缺省位数 = 最短精确有效数字数 − 1(round-trip 前缀,非任意最短十进制形)。
let cases = [
("(123.456).toExponential()", "1.23456e+2"),
("(2).toExponential()", "2e+0"),
("(0.1).toExponential()", "1e-1"),
("(0.3).toExponential()", "3e-1"),
("(1e-7).toExponential()", "1e-7"),
("(1.1e-32).toExponential()", "1.1e-32"),
("(1e20).toExponential()", "1e+20"),
("(1e-20).toExponential()", "1e-20"),
("(0.1+0.2).toExponential()", "3.0000000000000004e-1"),
("(123456789012345678901234567890.5).toExponential()", "1.2345678901234568e+29"),
("(42).toPrecision()", "42"),
("(0.0000001).toPrecision(3)", "1.00e-7"),
("(0.00000012345).toPrecision(2)", "1.2e-7"),
];
for (src, expected) in cases {
assert_eq!(str_of(&mut vm, src), expected, "for {}", src);
}
}
// 二进指数 q 为 limb 位宽整数倍(q ≡ 0 mod 32,偏置指数 e2 ≡ 19 mod 32)
// 的分数面:余数掩码边界恰落整 limb,digit 提取失真即此面(对照 node
// v20.19.2 逐位核定)。
#[test]
fn number_format_limb_aligned_fraction_digits() {
let mut vm = Vm::new();
let cases = [
// q = 64([2^-12, 2^-11) 量级)。
("(2 ** -12).toExponential(5)", "2.44141e-4"),
("(2 ** -12).toExponential(15)", "2.441406250000000e-4"),
// q = 32([2^20, 2^21) 量级)。
("(1048576.0000000002).toExponential(15)", "1.048576000000000e+6"),
("(1048576.0000000002).toFixed(10)", "1048576.0000000002"),
// 非受影响类的整数面守卫(小数值与 2^32 + 1)。
("(4.294967297).toFixed(2)", "4.29"),
("(4294967297).toFixed(2)", "4294967297.00"),
];
for (src, expected) in cases {
assert_eq!(str_of(&mut vm, src), expected, "for {}", src);
}
}