From d6acfdf04fbcdf82b4d292b960f45117c77a77fe Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Tue, 22 Sep 2026 13:24:16 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BD=99=E6=95=B0=E6=8E=A9=E7=A0=81?= =?UTF-8?q?=E6=95=B4=20limb=20=E8=BE=B9=E7=95=8C=E4=B8=A2=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E4=BD=8D=E4=BF=AE=E5=A4=8D=20digit=20=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E5=A4=B1=E7=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_builtins/src/number.rs | 13 +++++------ .../oxide_vm/tests/builtins_number_tests.rs | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/crates/oxide_builtins/src/number.rs b/crates/oxide_builtins/src/number.rs index b3ad0605..58810b90 100644 --- a/crates/oxide_builtins/src/number.rs +++ b/crates/oxide_builtins/src/number.rs @@ -380,7 +380,7 @@ fn coerce_to_integer_or_infinity(vm: &mut H, arg: JsValue) -> Result< // 传播进位。Rust `format!("{:.N$}")` 是 half-even,tie 面与规范分歧, // 仅 String() 路径可用。 -/// base 2^32 小整数(limb 升序),承载 f64 展开所需位宽(≤ 2^1127)。 +/// base 2^32 小整数(limb 升序),承载 f64 展开所需位宽(≤ 2^1024)。 #[derive(Clone)] struct Dec(Vec); @@ -441,7 +441,8 @@ impl Dec { self.trim(); } - /// 保留低 q 位(& (2^q − 1))。 + /// 保留低 q 位(& (2^q − 1))。q 为 32 整数倍时低 q 位恰为前 wm 整 limb, + /// limb wm 整体丢弃(先置 0 再由 trim 剥除),与 bm ≠ 0 臂统一截断。 fn rem_pow2(&mut self, q: u32) { let total = (self.0.len() * 32) as u32; if q >= total { @@ -449,9 +450,7 @@ impl Dec { } let wm = (q / 32) as usize; let bm = q % 32; - if bm != 0 { - self.0[wm] &= (1u32 << bm) - 1; - } + self.0[wm] = if bm != 0 { self.0[wm] & ((1u32 << bm) - 1) } else { 0 }; self.0.truncate(wm + 1); self.trim(); } @@ -491,10 +490,10 @@ fn significant_digits(x: f64, count: usize) -> (Vec, i32) { let b = x.to_bits(); let (m, p) = if (b >> 52) == 0 { // 次正规:值 = frac × 2^-1074(隐含位 0)。 - (b as u64, -1074) + (b, -1074) } else { // 正规:显式隐含位补齐尾数(指数位先掩掉,勿随位模式带入)。 - (((b as u64 & ((1u64 << 52) - 1)) | (1u64 << 52)), ((b >> 52) as i32) - 1075) + (((b & ((1u64 << 52) - 1)) | (1u64 << 52)), ((b >> 52) as i32) - 1075) }; if p >= 0 { // 整数面:V = M × 2^p,位数 D 定首位指数,弃低位后逐位提取。 diff --git a/crates/oxide_vm/tests/builtins_number_tests.rs b/crates/oxide_vm/tests/builtins_number_tests.rs index c8769eb2..db9b0efe 100644 --- a/crates/oxide_vm/tests/builtins_number_tests.rs +++ b/crates/oxide_vm/tests/builtins_number_tests.rs @@ -550,3 +550,25 @@ fn number_format_default_shortest_digits() { 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); + } +}