Reputation: 309
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;
}
Upvotes: 5
Views: 10317
Reputation: 106246
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;
}
Upvotes: 4
Reputation: 3446
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;
}
Upvotes: 0
Reputation: 6242
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.
Upvotes: 6
Reputation: 30146
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.
Upvotes: 3
Reputation: 306
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
Upvotes: 0