We help IT Professionals succeed at work.

atof inaccuracy

marcodalzotto
on
763 Views
Last Modified: 2012-08-14
Hi experts, I'm trying to convert a string to a double.

double d = atof("245.76");

The d variable become 245.75999999999999.

I need to have a double 245.76000000000000.

How suggest to fixit?
Comment
Watch Question

CERTIFIED EXPERT
Top Expert 2009

Commented:
What's the output of this program :

    #include <iostream>
   
    int main(void) {
      double d = atof("245.76");
      std::cout << d << std::endl;
      return 0;
    }

I get the correct result.

In any case, if you want exact precision, then a double is probably not what you want (it's a floating point number).

What do you need this for ? For certain applications, fixed point could be an option ...

Author

Commented:
I get the correct results.

I get the correct results even if I use the sprintf with "%f". because sprintf approximate.

The "245.76" string came from a MySQL database. And I need it as a double because I make some calculation.

I have tried to approximate the double value with the function form https://www.experts-exchange.com/Programming/Programming_Languages/C/Q_20412254.html?query=fprec&clearTAFilter=true

double fprec(double f,int prec)
{
 double p = pow(10,prec);
 return floor(f*p)/p;
}

But it returns the same thing.

Author

Commented:
It incredible! Even changing the variable with the debugger from "245.75999999999999" to "245.76000000000000" and if I put with the debugger "245.76000000000001" i get "245.76000000000002".

From that I can understand that is a resolution problem... but how can I sove it? How ca I fix the resolution for a double?

Author

Commented:
Sorry, my thought run fater than my keyboard :)

It incredible! Even changing the variable with the debugger from "245.75999999999999" to "245.76000000000000" I get again "245.75999999999999".
And if I put with the debugger "245.76000000000001" I get "245.76000000000002".

From that I can understand that is a resolution problem... but how can I sove it? How can I fix the resolution for a double?

Commented:
The problem is in a sense, unsolvable.

Numbers in the computer are usually stored in binary format.  For a floating-point number, it's a binary fraction with a binary power of two scale factor.  It's unlikely a decimal number will be exactly representable.

But you can round it-- use a print format with two decimal places and the print function will round the number to two decimal places.

Author

Commented:
>> But you can round it-- use a print format with two decimal places and the print function will round the number to two decimal places.

The round that makes printf is the issue. I try to explay what is appening in my project.

I have a variable soglia = -43.519999999999996 (it cames from a calculation)
I update to a MySQL database this variable through sprintf using %f
The sprintf transforms the soglia variable into -43.52
When I get back tha soglia value from the databse I have to transorm the "-43.52" in to a double.
Using the atof function like atof("-43.52") I get a double sogliadb = -43.520000000000003

So, If I want to compare the variable soglia = -43.519999999999996 with the variable I get form the database sogliadb = -43.520000000000003 I get difference.

How would you suggest to solve it?
>>>> The problem is in a sense, unsolvable.

Don't think so. The problem is the convertion to a double. If you would read the variable to a string and only convert for calculations you won't get the problem. Another 'solution' is to store the count of the fractional digits (of course you don't need to store it if you *know* that it is always 2) and make an appropriate rounding when outputting the number. Again, the problem is an output problem only. 245.75999999999999 is as good as 245.76 if used for calculations. For output you can do that:

#include <iomanip>
#include <iostream>
using namespace std;

     double d = atof("245.76");

     cout << fixed << setprecision(2) << d << endl;

You also could set the precision generally for cout by

     cout.precision(2);

If fixed or scientific is set, setprecision determines the number of fractional digits (digits right of the period). If not it determines the number of significant digits (5).

Regards, Alex
CERTIFIED EXPERT
Top Expert 2009

Commented:
>> I get the correct results.
Then I don't really understand your problem. Can you post your code that converts the string "245.76" to a double and when showing the double, it gives "245.75999999999999" ?

Anyway, like grg said : a double has only about 15 digits of precision (which is enough for most applications). If you need more precision, then you'll need to use a custom datatype instead.

As I asked earlier : what do you use this for ?
CERTIFIED EXPERT
Top Expert 2009
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
As stated above the floating point numbers are just a mathematical approximation.
Anyway the problem here seems to be in the output function.

Alex ( itsmeandnobodyelse ) proposed something like:

      cout << fixed << setprecision(2) << d << endl;

This is OK if the input string represents exactly 2 digits after the floating point, but if there are more, the output will be rounded.
A more general way to display the correct value of a floating point number in C++ would be:

        double d = atof("245.769");
        std::cout.setf(std::ios::floatfield);
        std::cout << d << std::endl;

This way the output is not rounded at all.
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
>>>> std::cout.setf(std::ios::floatfield);
>>>> std::cout << d << std::endl;

with that d was rounded to 6 significant digits (default).  

>>>> this way the output is not rounded at all.

That isn't true. Take      

   double d = atof("1245.769");

and you will see that the output is 1245.77.

   double d = atof("0.76999999");

gives  0.769999 as output.

Or worse:

       double d = atof("245.76124599999999");
       std::cout.setf(std::ios::floatfield);
       std::cout << d << std::endl;

would round to 245.76 !!!

So, with format flags you only can decide whether you want rounding dependent on the number of fractional digits or dependent on significant digits. If you only want to round differences that came from conversion decimal to binary and vice versa, you need to set the precision to 14 (significant digits) and set the format to floatfield.

       double d = atof("245.76124599999999");
       std::cout.setf(std::ios::floatfield);
       std::cout.precision(14);
       std::cout << d << std::endl;

So, only the combination of floatfield and precision(14) will give a correct result..

Regards, Alex

Author

Commented:
I think the easier way to do it is to follow the suggestion of AlexNek.
Another good way would be to compare setting the pecision as Infinity08 wrote.

Thanks for your suggestions.
>>>> I think the easier way to do it is to follow the suggestion of AlexNek.

You may have overread that he suggests, *Round* variable, multiply variable by 100 and store as integer.

You can do rounding by adding 0.5 before multiplying. Then 123.455 would turn to 123.460 and the integer*100 is 12346, correctly rounded. 123.544 would give 123.549 and finally 12354, what is correct as well. So 0, 1, 2, 3, 4 for the digit to round would be rounded down while 5, 6, 7, 8, 9 would round up. Following that the algorithm is

      int r100 = (int)((d +0.005)*100);

For output you need

   cout << r100/100 << '.' << r100%100;

It isn't easier or more transparent than  

   cout << fixed << precision(2) << d;  

Regards, Alex


>>>> by adding 0.5 before multiplying

It should mean 'by adding 0.5 after multiplying' or 'by adding 0.005 before multiplying'

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.