Implement sqrt with Newton vs binary search
Company: Uber
Role: Data Scientist
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
Quick Answer: This question evaluates numerical methods and algorithmic robustness, testing understanding of Newton’s method for root-finding, floating‑point stability and IEEE‑754 edge cases, and integer binary-search techniques for exact floor square roots within the Coding & Algorithms domain for a Data Scientist role.
sqrt_newton — principal square root via Newton's method
Constraints
- 0 <= x <= 1e308 (IEEE-754 double)
- tol > 0 (default 1e-12)
- Do not use sqrt or pow
- Raise ValueError for x < 0; return 0.0 for x == 0; return +inf for x == +inf
Examples
Input: (0.0,)
Expected Output: 0.0
Explanation: Special case: sqrt(0) is exactly 0.0.
Input: (1.0,)
Expected Output: 1.0
Explanation: sqrt(1) = 1; the guess starts at 1 and the residual is already 0.
Input: (4.0,)
Expected Output: 2.0
Explanation: Perfect square; Newton converges to the exact double 2.0.
Input: (16.0,)
Expected Output: 4.0
Explanation: Power-of-two perfect square; frexp-scaled start lands exactly on 4.0.
Input: (64.0,)
Expected Output: 8.0
Explanation: sqrt(64) = 8.0 exactly.
Input: (1024.0,)
Expected Output: 32.0
Explanation: Larger perfect square (2^10); converges to exact 32.0, exercising the scaled initial guess.
Input: (0.25,)
Expected Output: 0.5
Explanation: Sub-unit input (x < 1): normalizer max(x,1)=1; result 0.5 exactly.
Input: (0.0625,)
Expected Output: 0.25
Explanation: Small input edge case (1/16); exact result 0.25, confirming no underflow in the initial guess.
Hints
- Decompose x = m * 2**e with frexp; an initial guess of 2**(e//2) keeps you within a factor of sqrt(2) of the root, so no overflow even for x ~ 1e308.
- The Newton step for f(y)=y^2-x is y <- 0.5*(y + x/y); reuse u = x/y for both the stopping test and the update.
- Compute the residual as |y|*|y - x/y| instead of |y*y - x|: this avoids overflow of y*y near 1e154 and avoids cancellation between two large near-equal numbers.
isqrt_bisect — integer floor square root via binary search
Constraints
- 0 <= n < 2**63
- O(log n) iterations
- Must not compute mid*mid (overflow); compare mid <= n // mid
Examples
Input: (0,)
Expected Output: 0
Explanation: Edge case: floor(sqrt(0)) = 0 via the n<2 short-circuit.
Input: (1,)
Expected Output: 1
Explanation: Edge case: floor(sqrt(1)) = 1.
Input: (2,)
Expected Output: 1
Explanation: Non-perfect square: floor(sqrt(2)) = 1.
Input: (3,)
Expected Output: 1
Explanation: floor(sqrt(3)) = 1 (just below the next perfect square).
Input: (4,)
Expected Output: 2
Explanation: Perfect square boundary: floor(sqrt(4)) = 2.
Input: (8,)
Expected Output: 2
Explanation: floor(sqrt(8)) = 2 (8 < 9).
Input: (15,)
Expected Output: 3
Explanation: floor(sqrt(15)) = 3 (just below 16).
Input: (16,)
Expected Output: 4
Explanation: Perfect square: floor(sqrt(16)) = 4.
Input: (24,)
Expected Output: 4
Explanation: floor(sqrt(24)) = 4 (16 <= 24 < 25).
Input: (1000000,)
Expected Output: 1000
Explanation: Large perfect square: floor(sqrt(1_000_000)) = 1000.
Input: (9223372036854775807,)
Expected Output: 3037000499
Explanation: Boundary n = 2**63 - 1; floor(sqrt) = 3037000499. Confirms the mid <= n // mid guard prevents overflow at the largest allowed input.
Hints
- For 0 and 1, floor(sqrt(n)) == n, so short-circuit n < 2.
- From k = n.bit_length(), floor(sqrt(n)) < 2**ceil(k/2); start hi at 2**((k+1)//2) to cut iterations.
- Binary search on the candidate root; the overflow-safe predicate is mid <= n // mid, which is equivalent to mid*mid <= n for positive mid.