fix: 余数掩码整 limb 边界丢多余位修复 digit 展开失真

This commit is contained in:
2026-09-22 13:24:16 +00:00
parent 53029dbcc7
commit d6acfdf04f
2 changed files with 28 additions and 7 deletions
+6 -7
View File
@@ -380,7 +380,7 @@ fn coerce_to_integer_or_infinity<H: VmHost>(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<u32>);
@@ -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<u8>, 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 定首位指数,弃低位后逐位提取。
@@ -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);
}
}