Roots of a function along a span
64-bitFinds the parameters where a signed distance along the spline is zero.
sample n steps; bisect each sign change until b − a ≤ 1e-16 × max(1, |a|)
Each sign change is bisected until the bracket cannot shrink further or 200 halvings are done, so a found root is at the resolution of 64-bit numbers. The comment above the function mentions a Newton polish; the code refines by bisection only. Roots are found only where the samples change sign or dip below the tolerance.
/** Roots of f on [lo, hi] by sampling n steps, bisection on sign changes, Newton polish (numeric f′). */
function roots1(f, lo, hi, n, tolF) {
const out = [];
let u0 = lo, f0 = f(lo);
if (Math.abs(f0) <= tolF * 1e-3) out.push(lo);
let fPrev = null;
for (let i = 1; i <= n; i++) {
const u1 = lo + (hi - lo) * i / n, f1 = f(u1);
if (f1 === 0) out.push(u1);
else if (f0 !== 0 && (f0 < 0) !== (f1 < 0)) {
let a = u0, b = u1, fa = f0;
for (let it = 0; it < 200 && b - a > 1e-16 * Math.max(1, Math.abs(a)); it++) {
const m = (a + b) / 2, fm = f(m);
if (fm === 0) { a = b = m; break; }
if ((fm < 0) === (fa < 0)) { a = m; fa = fm; } else b = m;
}
out.push((a + b) / 2);
} else if (fPrev !== null && Math.abs(f0) < Math.abs(fPrev) && Math.abs(f0) < Math.abs(f1) && Math.abs(f0) <= tolF) {
// a touch: |f| has a local minimum near zero between samples — refine the minimum (golden section)
let a = lo + (hi - lo) * (i - 2) / n, b = u1;
for (let it = 0; it < 100; it++) {
const m1 = b - (b - a) * 0.618, m2 = a + (b - a) * 0.618;
if (Math.abs(f(m1)) < Math.abs(f(m2))) b = m2; else a = m1;
}
const m = (a + b) / 2;
if (Math.abs(f(m)) <= tolF * 1e-3) out.push(m);
}
fPrev = f0; u0 = u1; f0 = f1;
}
return out;
}