Given a positive number n and an integer p representing the desired precision, compute the square root of n accurate to p decimal places. The solution should avoid using built-in square root functions.
Note: Precision control is required to ensure the output is correctly rounded or truncated at p digits after the decimal.
Examples:
Input: n = 50, p = 3
Output: 7.071
Explanation: The square root of 50 up to 3 decimal places is 7.071Input: n = 10, p = 4
Output: 3.1622
Explanation: The square root of 10 up to 4 decimal places is 3.1622
Table of Content
[Expected Approach] Binary Search with Incremental Refinement - O(log n + p) Time and O(1) Space
The integer part of the square root is found first via binary search. Each decimal digit is then determined one at a time by testing whether adding a shrinking increment still keeps the square within n.
Step-by-Step Illustration:
Finding the integer part:
- start = 0, end = 50, ans = 0
- mid = 25 -> 25×25 = 625 > 50 -> end = 24
- mid = 12 -> 12×12 = 144 > 50 -> end = 11
- mid = 5 -> 5×5 = 25 ≤ 50 -> ans = 5, start = 6
- mid = 8 -> 8×8 = 64 > 50 -> end = 7
- mid = 6 -> 6×6 = 36 ≤ 50 -> ans = 6, start = 7
- mid = 7 -> 7×7 = 49 ≤ 50 -> ans = 7, start = 8
- start (8) > end (7), loop ends -> ans = 7
Refining digit 1 (increment = 0.1):
- (7 + 0.1)2 = 7.12 = 50.41 > 50 -> stop, ans stays 7
- increment becomes 0.01
Refining digit 2 (increment = 0.01):
- (7 + 0.01)2 = 7.012 = 49.14 ≤ 50 -> ans = 7.01
- (7.02)2 = 49.28 ≤ 50 -> ans = 7.02
- ... continues adding 0.01 ...
- (7.07)2 = 49.98 ≤ 50 -> ans = 7.07
- (7.08)2 = 50.13 > 50 -> stop, ans stays 7.07
- increment becomes 0.001
Refining digit 3 (increment = 0.001):
- (7.071)2 = 49.999 ≤ 50 -> ans = 7.071
- (7.072)2 = 50.013 > 50 -> stop, ans stays 7.071
- increment becomes 0.0001, loop ends (p = 3 digits done)
Final answer: 7.071
#include <bits/stdc++.h>
using namespace std;
double squareRoot(int n, int p) {
int start = 0, end = n;
double ans = 0.0;
// binary search for the integer part of the square root
while (start <= end) {
int mid = start + (end - start) / 2;
if ((long long)mid * mid <= n) {
ans = mid;
start = mid + 1;
} else {
end = mid - 1;
}
}
// refine the decimal part one digit at a time
double increment = 0.1;
for (int i = 0; i < p; i++) {
while ((ans + increment) * (ans + increment) <= n) {
ans += increment;
}
increment /= 10;
}
return ans;
}
int main() {
int n = 50, p = 3;
cout << fixed << setprecision(3) << squareRoot(n, p) << endl;
return 0;
}
class GfG {
static double squareRoot(int n, int p) {
int start = 0, end = n;
double ans = 0.0;
// binary search for the integer part of the square root
while (start <= end) {
int mid = start + (end - start) / 2;
if ((long) mid * mid <= n) {
ans = mid;
start = mid + 1;
} else {
end = mid - 1;
}
}
// refine the decimal part one digit at a time
double increment = 0.1;
for (int i = 0; i < p; i++) {
while ((ans + increment) * (ans + increment) <= n) {
ans += increment;
}
increment /= 10;
}
return ans;
}
public static void main(String[] args) {
int n = 50, p = 3;
System.out.printf("%.3f%n", squareRoot(n, p));
}
}
def squareRoot(n, p):
start, end = 0, n
ans = 0.0
# binary search for the integer part of the square root
while start <= end:
mid = start + (end - start) // 2
if mid * mid <= n:
ans = mid
start = mid + 1
else:
end = mid - 1
# refine the decimal part one digit at a time
increment = 0.1
for i in range(p):
while (ans + increment) * (ans + increment) <= n:
ans += increment
increment /= 10
return ans
n, p = 50, 3
print(f"{squareRoot(n, p):.3f}")
using System;
class GfG {
static double squareRoot(int n, int p) {
int start = 0, end = n;
double ans = 0.0;
// binary search for the integer part of the square root
while (start <= end) {
int mid = start + (end - start) / 2;
if ((long)mid * mid <= n) {
ans = mid;
start = mid + 1;
} else {
end = mid - 1;
}
}
// refine the decimal part one digit at a time
double increment = 0.1;
for (int i = 0; i < p; i++) {
while ((ans + increment) * (ans + increment) <= n) {
ans += increment;
}
increment /= 10;
}
return ans;
}
static void Main() {
int n = 50, p = 3;
Console.WriteLine(squareRoot(n, p).ToString("F3"));
}
}
function squareRoot(n, p) {
let start = 0, end = n;
let ans = 0.0;
// binary search for the integer part of the square root
while (start <= end) {
const mid = start + Math.floor((end - start) / 2);
if (mid * mid <= n) {
ans = mid;
start = mid + 1;
} else {
end = mid - 1;
}
}
// refine the decimal part one digit at a time
let increment = 0.1;
for (let i = 0; i < p; i++) {
while ((ans + increment) * (ans + increment) <= n) {
ans += increment;
}
increment /= 10;
}
return ans;
}
// Driver Code
const n = 50, p = 3;
console.log(squareRoot(n, p).toFixed(3));
Output
7.071
[Alternate Approach] Binary Search on Floating-Point Numbers - O(log(n/eps)) Time and O(1) Space
Instead of building the answer digit by digit, binary search is run directly on real numbers between 0 and n, narrowing the range until it converges to the square root. A fixed number of iterations ensures enough precision, and the result is then truncated to the required decimal places, with perfect squares handled separately using exact integer arithmetic to avoid floating-point boundary errors.
Step-by-Step Illustration:
Step 1: Check if n is a perfect square (integer arithmetic only)
- Compute floor(√50) using integer binary search → 7
- Check 7×7 = 49 ≠ 50 -> not a perfect square, proceed to binary search on doubles
Step 2: Binary search on doubles (low = 0, high = 50)
- Iteration 1: mid = 25.0, mid2 = 625.0 > 50 -> high = 25.0
- Iteration 2: mid = 12.5, mid2 = 156.25 > 50 -> high = 12.5
- Iteration 3: mid = 6.25, mid2 = 39.06 < 50 -> low = 6.25
- Iteration 4: mid = 9.375, mid2 = 87.89 > 50 -> high = 9.375
- Iteration 5: mid = 7.8125, mid2 = 61.04 > 50 -> high = 7.8125
- ... the interval keeps halving, narrowing in on 7.0710678... ...
- After 200 iterations (a fixed count, far more than needed), low and high have converged to within an extremely tiny gap of the true value
Step 3: Truncate to p = 3 decimal places
- Take the converged low value ≈ 7.0710678...
- Multiply by 1000 -> 7071.0678...
- Floor -> 7071
- Divide by 1000 -> 7.071
Final answer: 7.071
#include <bits/stdc++.h>
using namespace std;
// finds floor(sqrt(n)) using pure integer binary search, no built-in sqrt function
long long integerSqrt(long long n) {
long long low = 0, high = n, ans = 0;
while (low <= high) {
long long mid = low + (high - low) / 2;
if (mid * mid <= n) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return ans;
}
double squareRoot(int n, int p) {
// check for a perfect square first using exact integer arithmetic
long long intRoot = integerSqrt(n);
if (intRoot * intRoot == n) {
return (double) intRoot;
}
double low = 0.0, high = n;
// fixed iteration count converges far beyond any precision p could require
for (int i = 0; i < 200; i++) {
double mid = (low + high) / 2.0;
if (mid * mid < n) {
low = mid;
} else {
high = mid;
}
}
// truncate to p decimal places using integer arithmetic
double factor = pow(10, p);
double truncated = floor(low * factor) / factor;
return truncated;
}
int main() {
int n = 50, p = 3;
cout << fixed << setprecision(3) << squareRoot(n, p) << endl;
return 0;
}
class GfG {
// finds floor(sqrt(n)) using pure integer binary search, no built-in sqrt function
static long integerSqrt(long n) {
long low = 0, high = n, ans = 0;
while (low <= high) {
long mid = low + (high - low) / 2;
if (mid * mid <= n) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return ans;
}
static double squareRoot(int n, int p) {
// check for a perfect square first using exact integer arithmetic
long intRoot = integerSqrt(n);
if (intRoot * intRoot == n) {
return (double) intRoot;
}
double low = 0.0, high = n;
// fixed iteration count converges far beyond any precision p could require
for (int i = 0; i < 200; i++) {
double mid = (low + high) / 2.0;
if (mid * mid < n) {
low = mid;
} else {
high = mid;
}
}
// truncate to p decimal places using integer arithmetic
double factor = Math.pow(10, p);
double truncated = Math.floor(low * factor) / factor;
return truncated;
}
public static void main(String[] args) {
int n = 50, p = 3;
System.out.printf("%.3f%n", squareRoot(n, p));
}
}
def integerSqrt(n):
# finds floor(sqrt(n)) using pure integer binary search, no built-in sqrt function
low, high, ans = 0, n, 0
while low <= high:
mid = low + (high - low) // 2
if mid * mid <= n:
ans = mid
low = mid + 1
else:
high = mid - 1
return ans
def squareRoot(n, p):
# check for a perfect square first using exact integer arithmetic
intRoot = integerSqrt(n)
if intRoot * intRoot == n:
return float(intRoot)
low, high = 0.0, float(n)
# fixed iteration count converges far beyond any precision p could require
for i in range(200):
mid = (low + high) / 2.0
if mid * mid < n:
low = mid
else:
high = mid
# truncate to p decimal places using integer arithmetic
factor = 10 ** p
truncated = int(low * factor) / factor
return truncated
n, p = 50, 3
print(f"{squareRoot(n, p):.3f}")
using System;
class GfG {
// finds floor(sqrt(n)) using pure integer binary search, no built-in sqrt function
static long integerSqrt(long n) {
long low = 0, high = n, ans = 0;
while (low <= high) {
long mid = low + (high - low) / 2;
if (mid * mid <= n) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return ans;
}
static double squareRoot(int n, int p) {
// check for a perfect square first using exact integer arithmetic
long intRoot = integerSqrt(n);
if (intRoot * intRoot == n) {
return (double) intRoot;
}
double low = 0.0, high = n;
// fixed iteration count converges far beyond any precision p could require
for (int i = 0; i < 200; i++) {
double mid = (low + high) / 2.0;
if (mid * mid < n) {
low = mid;
} else {
high = mid;
}
}
// truncate to p decimal places using integer arithmetic
double factor = Math.Pow(10, p);
double truncated = Math.Floor(low * factor) / factor;
return truncated;
}
static void Main() {
int n = 50, p = 3;
Console.WriteLine(squareRoot(n, p).ToString("F3"));
}
}
// finds floor(sqrt(n)) using pure integer binary search, no built-in sqrt function
function integerSqrt(n) {
let low = 0, high = n, ans = 0;
while (low <= high) {
const mid = low + Math.floor((high - low) / 2);
if (mid * mid <= n) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return ans;
}
function squareRoot(n, p) {
// check for a perfect square first using exact integer arithmetic
const intRoot = integerSqrt(n);
if (intRoot * intRoot === n) {
return intRoot;
}
let low = 0.0, high = n;
// fixed iteration count converges far beyond any precision p could require
for (let i = 0; i < 200; i++) {
const mid = (low + high) / 2.0;
if (mid * mid < n) {
low = mid;
} else {
high = mid;
}
}
// truncate to p decimal places using integer arithmetic
const factor = Math.pow(10, p);
const truncated = Math.floor(low * factor) / factor;
return truncated;
}
// Driver Code
const n = 50, p = 3;
console.log(squareRoot(n, p).toFixed(3));
Output
7.071