C++ double value losing precision when multiplied?
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!
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.
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.
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.
精彩评论