Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

Coding · Binary search

Searching real numbers

Lesson · 8 min read · Binary search · Searching a monotone condition

Over real numbers there is no last candidate, only a range that keeps shrinking. Stop after a fixed number of halvings, not when the range is narrow enough.

Bisection

When the answer is a real number, every candidate has neighbours arbitrarily close, so the loop cannot end when lo meets hi. Instead it keeps the invariant (the answer lies in [lo, hi]) and halves the range a fixed number of times. Both updates are simply lo = mid or hi = mid: no ±1, because there is no next candidate.

Bisection for a monotone test on reals

bisect(lo, hi, test)

  1. Invariant: lo and hi lie on opposite sides of the boundary, so the answer is in [lo, hi].
  2. Repeat 100 times: mid = (lo + hi) / 2; whichever end is on the same side as mid moves to mid.
  3. Return hi (or lo: after 100 halvings they agree to the last bit).

Eight rounds of the cube root of 10

The test is mid³ ≤ 10 and the range starts at [0, 10]. Each row is the loop run for real.

Roundlohimidmid³Update
10.00000010.0000005.000000125.0000hi = mid
20.0000005.0000002.50000015.6250hi = mid
30.0000002.5000001.2500001.9531lo = mid
41.2500002.5000001.8750006.5918lo = mid
51.8750002.5000002.18750010.4675hi = mid
61.8750002.1875002.0312508.3809lo = mid
72.0312502.1875002.1093759.3856lo = mid
82.1093752.1875002.1484389.9167lo = mid

After eight rounds the range is 10 / 2⁸ ≈ 0.039 wide. Run on, and after 100 rounds lo has settled at 2.154434690032, the cube root of 10 to the precision a double can hold.

Why "while hi − lo > ε" can hang

A double has 53 significant bits, so between 2²⁹ and 2³⁰ (about 5.4 × 10⁸ to 1.07 × 10⁹) neighbouring doubles are 2⁻²³ ≈ 1.2 × 10⁻⁷ apart. If the answer is near 10⁹ and the loop waits for hi − lo to drop below 10⁻⁹, it never will: once lo and hi are neighbours, mid equals one of them and the range stops shrinking.

A fixed count avoids the question. Each round halves the width, so to get from a width W to a precision ε takes log₂(W / ε) rounds: 10⁹ down to 10⁻⁶ is about 50. A hundred rounds is enough for any double range and costs nothing.

If the precision required is fixed and coarse (prices to the cent, times to the millisecond), scale to integers and use the integer templates instead. Exact arithmetic beats a tolerance.

Where it is used

  • Roots of increasing functions: the rate at which an investment reaches a target, the speed at which a schedule just fits. The test is "is f(mid) at least the target?"
  • Maximising an average. "The largest average of any subarray of length at least k" has a monotone test: an average of at least m exists exactly when some subarray of a[i] − m of that length has a non-negative sum, which prefix sums check in O(n). Bisection on m wraps it.
  • Geometry, such as the time at which two moving points come within a distance, when the distance only decreases and then increases (a different search, on the slope, as in the peaks module).
TimeO(iterations × cost of the test)
SpaceO(1) beyond the test

With a fixed 100 iterations, the cost is 100 tests whatever the range.