Merge PR #1432: Stop BINOM.INV/CRITBINOM hanging when trials exceed 2^53

This commit is contained in:
aiongg
2026-10-22 02:34:45 +00:00
+22
View File
@@ -1243,6 +1243,10 @@ fn binom_inv(a: &[Arg], _: C) -> R<Value> {
}
while hi - lo > 1.0 {
let mid = ((lo + hi) / 2.0).floor();
// Above 2^53 adjacent f64 values are more than 1 apart, so the gap can stop shrinking.
if mid <= lo || mid >= hi {
break;
}
if binom_cdf(mid, n, p) >= alpha {
hi = mid;
} else {
@@ -2624,6 +2628,24 @@ mod tests {
is_err(ev("F.DIST", vec![n(1.0), n(0.0), n(4.0), b(true)]), CellError::Num);
}
#[test]
fn binom_inv_huge_trials_returns() {
// Above 2^53 neighbouring f64 values are more than 1 apart; the bisection must still end.
let cases: [(&str, f64, f64, f64); 3] = [("BINOM.INV", 1e17, 0.5, 0.5), ("CRITBINOM", 1e300, 1.0, 0.5), ("BINOM.INV", 1e300, 0.5, 0.5)];
for (name, trials, p, alpha) in cases {
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let r = ev(name, vec![n(trials), n(p), n(alpha)]);
let _ = tx.send(r);
});
match rx.recv_timeout(std::time::Duration::from_secs(5)) {
Ok(Value::Number(_)) | Ok(Value::Error(CellError::Num)) => {}
Ok(other) => panic!("{name}({trials},{p},{alpha}) returned {other:?}"),
Err(_) => panic!("{name}({trials},{p},{alpha}) did not return within 5 s"),
}
}
}
#[test]
fn discrete() {
close_tol(ev("BINOM.DIST", vec![n(6.0), n(10.0), n(0.5), b(false)]), 0.205078125, 1e-12);