marcodalzotto
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?
double d = atof("245.76");
The d variable become 245.75999999999999.
I need to have a double 245.76000000000000.
How suggest to fixit?
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.
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.
ASKER
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?
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?
ASKER
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?
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?
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.
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.
ASKER
>> 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 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
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 ?
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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::f loatfield) ;
std::cout << d << std::endl;
This way the output is not rounded at all.
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::f
std::cout << d << std::endl;
This way the output is not rounded at all.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
>>>> std::cout.setf(std::ios::f loatfield) ;
>>>> 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::f loatfield) ;
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::f loatfield) ;
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
>>>> 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::f
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::f
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
ASKER
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.
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
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'
It should mean 'by adding 0.5 after multiplying' or 'by adding 0.005 before multiplying'
#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 ...