Skip to content

Commit 12ea6bb

Browse files
committed
fix: avoid u64 overflow in solve_itp for large nmax values
When epsilon is very small (e.g. 1e-24), nmax can exceed 63. The expression `(1u64 << nmax) as f64` then overflows: debug builds panic, release builds silently produce the wrong value because x86 masks the shift amount to 6 bits (so << 64 wraps to << 0 = 1). Replace with `(nmax as f64).exp2()` which is mathematically identical (2^nmax) but uses f64 arithmetic, handling nmax up to ~1023 before saturating to +inf — well beyond any practical epsilon. Add a regression test with epsilon=1e-24 (nmax≈79) that panics on the original code in debug mode.
1 parent 838b69e commit 12ea6bb

1 file changed

Lines changed: 20 additions & 1 deletion

File tree

kurbo/src/common.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,8 @@ pub(crate) fn solve_itp_fallible<E>(
710710
) -> Result<(f64, f64), E> {
711711
let n1_2 = (((b - a) / epsilon).log2().ceil() - 1.0).max(0.0) as usize;
712712
let nmax = n0 + n1_2;
713-
let mut scaled_epsilon = epsilon * (1u64 << nmax) as f64;
713+
let powi_nmax: i32 = nmax.min(i32::MAX as usize).try_into().unwrap_or(i32::MAX);
714+
let mut scaled_epsilon = epsilon * 2.0_f64.powi(powi_nmax);
714715
while b - a > 2.0 * epsilon {
715716
let x1_2 = 0.5 * (a + b);
716717
let r = scaled_epsilon - 0.5 * (b - a);
@@ -1097,6 +1098,24 @@ mod tests {
10971098
assert!(f(x).abs() < 6e-12);
10981099
}
10991100

1101+
#[test]
1102+
fn test_solve_itp_large_nmax_does_not_overflow() {
1103+
let ya = -0.5;
1104+
let yb = 0.5;
1105+
let mut calls = 0usize;
1106+
let mut f = |x: f64| {
1107+
calls += 1;
1108+
assert!(
1109+
calls <= 10_000,
1110+
"solve_itp made too many function evaluations without converging"
1111+
);
1112+
x - 0.5
1113+
};
1114+
let x = solve_itp(&mut f, 0.0, 1.0, 1e-24, 0, 0.2, ya, yb);
1115+
assert!(x.is_finite());
1116+
assert!((x - 0.5).abs() <= 1e-12);
1117+
}
1118+
11001119
#[test]
11011120
fn test_inv_arclen() {
11021121
use crate::{ParamCurve, ParamCurveArclen};

0 commit comments

Comments
 (0)