tags:

views:

121

answers:

3

I am trying to create a function to find the square root of a number. For debugging purposes, it has instructions to print current variable values. The function squareRoot accepts two arguments, x and t. It then declares and initializes n and s. n is the amount to add or subtract, halving every time it is used. s is what is thought to be the current square root. When running, I can clearly see that n is adjusting correctly. However, s stops changing when the first four digits are correct. I am using this call in main():

cout << squareRoot(1000, 10) << "\n";

This should print the square root of 1000 to the nearest tenth, but two weird things happen:

  • It doesn't stop at 31.6.
  • It stops at 4 digits!

My theory as to why it stops at four digits is this: In multiplying, s loses some of its precision. Is this true? If so, can you tell me how to correct it? If not, what is causing this and how can I correct that?

I tried to solve it already by using another variable, s1, which would be multiplied and checked. Then s would be incremented by n, and s1 synchronized with s. This didn't work, so I went back to the original code.

My code is as follows:

#include <iostream>
using namespace std;

double squareRoot(double x, int t) {
    double s = 0;
    double n = 0.1;
    while ((s*s) <= x) {
            s += n;
            n *= 2;
            cout << n << "\n" << s << "\n";
    }
    cout << "\n";
    s -= n;
    // Keep changing until margin of error is reached
    while ((((s*s) - x) < (1/t)) || ((x - (s*s) < (1/t)))) {
        // If too high, lower s
        if ((s*s) <= x) {
            s += n;
            n /= 2;
            cout << "Adding 1/2 of previous n\n";
        }
        // If too low, raise s
        else if ((s*s) >= x) {
            s -= n;
            n /= 2;
            cout << "Subtracting 1/2 of previous n\n";
        }
        cout << s << "\n" << n << "\n\n";
    }
    return s;
}

I am running Windows 7 64 bit, MSVC++ 2008 Express. Thank you in advance for all answers!

+2  A: 

It converges to the correct square root, but cout only prints six significant digits by default: 31.xxxx.

Also notice that your termination check does not work because (1/t) will always evaluate to 0 for t>1. Use 1./t instead.

Hmm... When trying your solution to the termination check problem, my loop never ends. Before, it did end. Also, when printing <code>n</code>, the value is correct, just not for <code>s</code>.
emasonsbro
And how would I go about changing this default of six digits?
emasonsbro
@emasonsbro Loop never ends because it has two problems: see my answer for another one. (it's the case when two bugs in the code compensate each other :))
Nikita Rybak
+1  A: 

The reason loop isn't stopping is that loop condition isn't correct. You have

while ((current - target < precision) || (target - current < precision)) {
    ...
}

But either value current - target or value target - current will be <= 0. And 0 < precision. Thus you have equivalent of while (true) {...} loop.
You need something like

while (abs(current - target) > precision) {
    ...
}

(don't know exact function for getting absolute value in C++)

edit
Just noticed, hamster3null wrote about 1/t before me.

Nikita Rybak
`fabs()` is the function for absolute value of a double in C, assuming you have `<math.h>` included. Of course, the question is about C++.
Jonathan Leffler
@Jonathon - meaning you should `#include <cmath>` instead.
Steve314
@Jonathan Thanks, I corrected language and another mistake in the code.
Nikita Rybak
@Steve: I guess so...I added the C++ rider and didn't go back to fix the `#include`.
Jonathan Leffler
Thank you for this. However, I think I will instead just use muntoo's code, as it is faster.
emasonsbro
+1  A: 

Unrelated, but your sqrt algorithm can be sped up by using an existing one, such as Newton's Method.

It goes like this:

double mySqrt(double x, unsigned long accuracy = 10)
{
   if(x < 0.0)
      return(-1.0);
   double retval = 1.0;
   for(unsigned long rep = 0; rep < accuracy; rep++)
   {
      retval = ((x / retval) + retval) / 2.0;
   }
   return(retval);
}

Newton's method also works for cube roots, etc. For decimal exponents, look up the Binomial Theorem.

muntoo
Thank you very much for this code, it works just fine.
emasonsbro
You're welcome. I tried this before, so I thought I might share with you what I found out.
muntoo
Woops. I used "/ 2" instead of "/ 2.0". It's corrected now. And thanks to whoever edited it to make the code in a codebox.
muntoo
I just found out how to use `cout.precision()` to set more than 6 digits! Thank you very much for this!
emasonsbro
I said earlier I changed it to `/ 2.0` from `/ 2`, but I forgot to... Can you change it to `retval = ((x / retval) + retval) / 2.0;`, so it doesn't cause any trouble in the future (on different compilers, etc)?
muntoo