mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-25 19:21:11 +08:00
fix: 余数掩码整 limb 边界丢多余位修复 digit 展开失真
This commit is contained in:
@@ -380,7 +380,7 @@ fn coerce_to_integer_or_infinity<H: VmHost>(vm: &mut H, arg: JsValue) -> Result<
|
|||||||
// 传播进位。Rust `format!("{:.N$}")` 是 half-even,tie 面与规范分歧,
|
// 传播进位。Rust `format!("{:.N$}")` 是 half-even,tie 面与规范分歧,
|
||||||
// 仅 String() 路径可用。
|
// 仅 String() 路径可用。
|
||||||
|
|
||||||
/// base 2^32 小整数(limb 升序),承载 f64 展开所需位宽(≤ 2^1127)。
|
/// base 2^32 小整数(limb 升序),承载 f64 展开所需位宽(≤ 2^1024)。
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Dec(Vec<u32>);
|
struct Dec(Vec<u32>);
|
||||||
|
|
||||||
@@ -441,7 +441,8 @@ impl Dec {
|
|||||||
self.trim();
|
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) {
|
fn rem_pow2(&mut self, q: u32) {
|
||||||
let total = (self.0.len() * 32) as u32;
|
let total = (self.0.len() * 32) as u32;
|
||||||
if q >= total {
|
if q >= total {
|
||||||
@@ -449,9 +450,7 @@ impl Dec {
|
|||||||
}
|
}
|
||||||
let wm = (q / 32) as usize;
|
let wm = (q / 32) as usize;
|
||||||
let bm = q % 32;
|
let bm = q % 32;
|
||||||
if bm != 0 {
|
self.0[wm] = if bm != 0 { self.0[wm] & ((1u32 << bm) - 1) } else { 0 };
|
||||||
self.0[wm] &= (1u32 << bm) - 1;
|
|
||||||
}
|
|
||||||
self.0.truncate(wm + 1);
|
self.0.truncate(wm + 1);
|
||||||
self.trim();
|
self.trim();
|
||||||
}
|
}
|
||||||
@@ -491,10 +490,10 @@ fn significant_digits(x: f64, count: usize) -> (Vec<u8>, i32) {
|
|||||||
let b = x.to_bits();
|
let b = x.to_bits();
|
||||||
let (m, p) = if (b >> 52) == 0 {
|
let (m, p) = if (b >> 52) == 0 {
|
||||||
// 次正规:值 = frac × 2^-1074(隐含位 0)。
|
// 次正规:值 = frac × 2^-1074(隐含位 0)。
|
||||||
(b as u64, -1074)
|
(b, -1074)
|
||||||
} else {
|
} 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 {
|
if p >= 0 {
|
||||||
// 整数面:V = M × 2^p,位数 D 定首位指数,弃低位后逐位提取。
|
// 整数面: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);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user