I can not figure out why this algorithm enters an infinite loop if the number entered is over 12 digits. Can anyone see why it will never end? Thanks. I just updated the algorithm to use the fabs() function and still get an infinite loop.
double squareroot(double x)
{ /* computes the square root of x */
/* make sure x is not negative .. no math crimes allowed! */
assert( x >= 0 );
if (x==0) return 0;
/* the sqrt must be between xhi and xlo */
double xhi = x;
double xlo = 0;
double guess = x/2;
/* We stop when guess*guess-x is very small */
while (abs(guess*guess-x) > 0.00001 )
{
if (guess*guess > x){
xhi = guess;
}
else {
xlo = guess;
}
guess = (xhi + xlo)/2;
}
return guess;
}
5 Answers 5
I believe you should use relative error for the termination, and not the absolute error.
while (abs((guess*guess-x) / guess) > 0.00001)
Otherwise it will take very long time (it's not an infinite loop) to compute square root of very long values.
http://en.wikipedia.org/wiki/Approximation_error
Cheers!
EDIT: moreover, as pointed below in the comments, it is worthy to check if the guess was already guessed in order to avoid infinite loop with some specific corner cases.
3 Comments
xhi and xlo are adjacent, the midpoint will always be one of them. When xhi - xlo is greater than the chosen epsilon, you get an infinite loop.guess == xhi || guess == xlo)I suggest waiting until you've got a stable answer, rather than fiddling with epsilon values:
double squareroot(double x)
{
if (x < 1) return 1.0 / squareroot(x); // MSalter's general solution
double xhi = x;
double xlo = 0;
double guess = x/2;
while (guess * guess != x)
{
if (guess * guess > x)
xhi = guess;
else
xlo = guess;
double new_guess = (xhi + xlo) / 2;
if (new_guess == guess)
break; // not getting closer
guess = new_guess;
}
return guess;
}
4 Comments
sqrt(x) lies in the range [0,x] while sqrt(0.25) > 0.25. The first guess is 0.125, the second guess 0.1875 etc. It appears xlo converges to xhi and the loop terminates when xlo=xhi=guess=0.25, which obviously is not 0.5sqrt(1/x) = 1/sqrt(x), so just add if (x<1) return 1.0/squareroot(x);.This is not a direct answer to your question, but an alternative solution.
You can use Newton's method for finding roots:
assert(x >= 0);
if (x == 0)
return 0;
double guess = x;
for (int i=0; i<NUM_OF_ITERATIONS; i++)
guess -= (guess*guess-x)/(2*guess);
return guess;
24 iterations should get you a good enough approximation, but you can also check the absolute difference.
Comments
http://floating-point-gui.de/errors/comparison/
Heh, looks like you are not supposed to use abs() like that. There are some cases where it should stop but i won't since it's limited precision.
Instead use fabs()
http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
4 Comments
std::abs overloaded function.<math>.std::abs (and possibly abs in header <cmath>. See en.cppreference.com/w/cpp/numeric/math/fabs. It is important to post an MCVE, and if possible, use std::abs explicitly.I'd say that when numbers are big enough you can't use absolute epsilon value because it doesn't fit into precision.
Try to use relative comparison instead. Consider following function to check if 2 doubles are equal:
bool are_eql(double n1, double n2, double abs_e, double rel_e)
{
// also add check that n1 and n2 are not NaN
double diff = abs(n1 - n2);
if (diff < abs_e)
return true;
double largest = max(abs(n1), abs(n2));
if (diff < largest * rel_e)
return true;
return false;
}
1 Comment
abs(n1-n2). I suppose largest should be abs(max(n1,n2)) - what if both are negative? In your code, largest can be negative, in which case diff>largest (as +0.01 > -100.0)
absis a function for integers. Did you meanfabs?guess*guess? that would be big..Cbut inC++, there is also a floting point overload forabsin<cmath>.