Link to home
Start Free TrialLog in
Avatar of marcodalzotto
marcodalzottoFlag for Italy

asked on

atof inaccuracy

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?
Avatar of Infinity08
Infinity08
Flag of Belgium image

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 ...
Avatar of marcodalzotto

ASKER

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/questions/20412254/Controlling-float-precision.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.

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?
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?
Avatar of grg99
grg99

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.

>> 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
>> 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 ?
SOLUTION
Avatar of Infinity08
Infinity08
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>>> 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
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'