Bisection Method — Midpoint Overflow at 1e308 Boundaries
Using (a+b)/2 overflows to Infinity when bracketing values near 1e308.
20+ years shipping performance-critical code where algorithms decide the bill. Written from production experience, not tutorials.
- Bisection halves the search interval each step using sign change (IVT)
- Requires f(a) * f(b) < 0 — no bracket, no guarantee
- Linear convergence: 1 bit of precision per iteration (~34 steps for 10^-10)
- Floating-point midpoint can drift — use (a+b)/2 not a+b/2
- Production insight: scipy.optimize.brentq is the real workhorse
- Biggest mistake: assuming it finds all roots (it finds only one per bracket)
If you know a function is negative at point a and positive at point b, it must cross zero somewhere between them — the Intermediate Value Theorem. Bisection exploits this: check the midpoint. Is it negative or positive? The root is in one half — discard the other. Repeat, halving the search interval each time. Slow but bulletproof — it always finds the root.
Bisection is the simplest application of the Intermediate Value Theorem: if f is continuous and f(a) < 0 and f(b) > 0, then somewhere in [a,b] the function crosses zero. The algorithm just applies this fact repeatedly, halving the interval each time.
The guaranteed convergence of bisection makes it invaluable as a safety net. Bisection cannot diverge, cannot oscillate, cannot fail as long as the initial bracket is valid. This is why Brent's method — the gold standard for numerical root finding, used in scipy.optimize.brentq — combines bisection as a fallback with Newton-Raphson's quadratic convergence: the speed of Newton when near the root, the reliability of bisection everywhere else.
How the Bisection Method Actually Finds Roots
The bisection method is a root-finding algorithm that repeatedly halves an interval known to contain a sign change. Given a continuous function f(x) and an interval [a, b] where f(a) and f(b) have opposite signs, it computes the midpoint m = (a + b) / 2, evaluates f(m), and replaces either a or b with m depending on the sign of f(m). This guarantees linear convergence: each iteration halves the error, so 10 iterations give about 3 decimal digits of accuracy.
In practice, the method is simple, robust, and guaranteed to converge — but it's slow. It requires O(log((b-a)/ε)) iterations for tolerance ε. Unlike Newton's method, it needs no derivative and works on any continuous function. The critical implementation detail: the midpoint calculation (a + b) / 2 can overflow when a and b are near Double.MAX_VALUE (~1.8e308). Use a + (b - a) / 2 instead.
Use bisection when you need guaranteed convergence and can tolerate linear speed — for example, calibrating sensor thresholds, finding breakpoints in piecewise models, or as a fallback when derivative-based methods fail. It's the safety net of numerical root-finding: never diverges, never needs tuning, but never fast.
Algorithm and Implementation
Invariant: f(a) and f(b) have opposite signs → root in [a,b]. Each step halves the interval.
There's a subtle trap in the midpoint calculation. The naive (a+b)/2 can overflow when a and b are large and opposite signs. Production code uses a + (b-a)/2, which is safe.
The termination check uses abs(b-a)/2 < tol, not abs(f(mid)) < tol — that's a common source of early false convergence. The interval width is the true error bound.
Error Analysis and Convergence
After n iterations, the interval width is (b-a)/2^n. The error in the root position is at most this width.
To achieve precision ε: n ≥ log2((b-a)/ε) iterations.
For [1,2] with ε=10^-10: n ≥ log2(1/10^-10) = 10×log2(10) ≈ 33.2 → 34 iterations.
This is linear convergence — one binary digit per iteration. Compare to Newton-Raphson which doubles correct digits each step. But bisection never fails to get those digits, Newton sometimes doesn't converged at all.
Bisection vs Newton-Raphson
| Property | Bisection | Newton-Raphson |
|---|---|---|
| Convergence | Linear (1 bit/step) | Quadratic (doubles digits) |
| Iterations for 10^-10 | ~34 | ~5 |
| Requires derivative | No | Yes |
| Guaranteed convergence | Yes (given bracket) | No |
| Initial requirement | Bracket [a,b] | Single guess x0 |
Best practice: Use bisection to get close (10^-3 precision), then switch to Newton-Raphson for fast final convergence. This is essentially Brent's method.
But there's a hidden cost: Newton-Raphson requires computing the derivative. For functions that are expensive to evaluate or have no analytic derivative, bisection remains the only option.
When Bisection Fails: Invalid Brackets and Discontinuities
Bisection's guarantee depends on two assumptions: 1. f(a) and f(b) have opposite signs 2. f is continuous on [a,b]
If the function has a vertical asymptote (like 1/x at 0), a sign change doesn't guarantee a root — you'll converge to a singularity. Similarly, if there are multiple roots inside the interval, bisection will only find one (the one that triggers the sign change at each step).
Another failure mode: the bracket is valid but the function is flat at the root, causing f(mid) to be exactly zero prematurely. Your abs(b-a)/2 < tol check saves you here, but if you mistakenly use abs(f(mid)) < tol you might converge to a point where f is tiny but the interval is still large.
Brent's Method: The Production-Grade Hybrid
Brent's method combines bisection's reliability with Newton-Raphson's speed. It uses inverse quadratic interpolation when possible, but falls back to bisection when interpolation would go out of bounds or converge too slowly.
The result: guaranteed convergence, and typically converges superlinearly (faster than bisection, slower than pure Newton). In practice it's the default choice for one-dimensional root-finding in scipy, MATLAB's fzero, and many other numerical libraries.
Implementation complexity is higher than plain bisection, but you don't write it yourself — you use a library. Understanding the internals helps when debug convergence issues.
Assumptions That Kill Your Root-Finding Run
The bisection method isn't magic. It's a direct application of the Intermediate Value Theorem, and that theorem comes with non-negotiable preconditions. Skip the sanity checks, and you'll be debugging silent garbage.
First, your function must be continuous on the closed interval [a, b]. Not "mostly continuous" or "continuous enough." Fully continuous. If there's a jump discontinuity at the midpoint, the method will happily converge on the wrong side of the crack.
Second, f(a) and f(b) must have opposite signs. f(a) * f(b) < 0. That's the root guarantee. If both are positive or both negative, you have zero confidence a root exists inside the bracket. The method will narrow the interval mindlessly, giving you a precise answer to a question you shouldn't have asked.
Third, you need exactly one root in that interval. Multiple roots with the same sign? The theorem is silent. The method will find one, but you won't know which, or if it even found the one you wanted.
Always validate these three before the first iteration. Production code is not a guessing game.
The Only Example You'll Ever Need: x³ - x - 2
Let's make this concrete. You're chasing the root of f(x) = x³ - x - 2. This polynomial has one real root near 1.5. We'll use bisection to find it to three decimal places.
Pick a = 1, b = 2. f(1) = -2, f(2) = 4. Signs are opposite. We're good. The midpoint c = 1.5. f(1.5) = -0.125. Still negative. Since f(a) and f(c) share a sign, the root must be between c and b. So we shift: a = 1.5, b stays 2.
Next iteration: c = 1.75. f(1.75) = 1.609. Positive. Now f(a) is negative, f(c) is positive → root between a and c. Set b = 1.75.
Keep halving. By iteration 10, your interval width is under 0.001. The midpoint is 1.521. f(1.521) ≈ 0.006. That's your root to three decimals.
Each iteration cuts the error in half. Linear convergence is slow but reliable. You trade speed for certainty. Use it when correctness matters more than cycles.
Midpoint Overflow and Sign Error in Embedded Systems
- Midpoint computation: always use a + (b-a)/2 when interval width may be large.
- Never assume integer arithmetic rules hold in floating-point.
- Validate the bracket at the start — f(a)*f(b) > 0 means no root guaranteed.
Key takeaways
Common mistakes to avoid
3 patternsUsing f(mid) == 0 as termination condition
Forgetting to validate the initial bracket
Applying bisection to find multiple roots simultaneously
Practice These on LeetCode
Interview Questions on This Topic
How many bisection steps are needed to find a root to within 10^-6 on the interval [0, 1]?
Frequently Asked Questions
20+ years shipping performance-critical code where algorithms decide the bill. Written from production experience, not tutorials.
That's Numerical Analysis. Mark it forged?
5 min read · try the examples if you haven't