Reputation: 500
I have a system that I can't change now, is use float to store informations.
I had a problem with rounding error. Example:
std::string floatToStr(float d)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(15) << d;
return ss.str();
}
float val723 = 0.575f;
std::cout << floatToStr(val723) << std::endl;
The result is
0.574999988079071
I can correct this with a string process.
std::string doubleToStr(double d, int precision)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(precision) << d;
return ss.str();
}
double val945 = (double)0.575f;
std::cout << doubleToStr(atof(doubleToStr(val945, 4).c_str()), 15) << std::endl;
The result is:
0.575000000000000
But this is a costy solution. Is there a any better solution which can be use in realtime process? (Actually I don't need to use it in my real time code but I prepare myself if I will have to use in real time code.)
Edit1: I understand I can use 6 or 7 digits after the decimal point for float type.
Upvotes: 0
Views: 2658
Reputation:
Ummm. You asked for 15 digits after the decimal point, and that's what it's given you. Seems a bit contrary to complain about that !
The fact that the value is a float, means that 6 decimal digits is about the limit of the precision available (7, if you are confident that you have almost no rounding errors). Asking for 15 decimals, after the '.' is showing you the state of the ls bits of the float, which are affected by rounding and representation errors.
What you appear to want to do is present the value of your float as if it was a double, good to 15 places of decimal, then you need to round the double version of the float to the 6 (or, if you feel brave) 7 decimal digits of precision. This is very similar to what the binary to decimal conversion does anyway, but you need to prepare the double copy of the value, ready for output.
This is not a million miles from getting the string version of the value, to 6 digits, and converting that to double. You can establish for yourself which is quicker !
Unfortunately, there remains an issue. You are asking for 15 decimals after the '.'. This is going to show up the representation errors for values bigger than 10.0, and possibly for values between 1.0 and 10.00. For example: if your float value is (say) 57.51234f (whose actual value is approximately 57.512340545654297); the code below will deliver 575124.0 / 10000.0 as a double, which when output gives: 57.512300000000003 -- because you are asking for approximately 1 digit more than the double has to give. (For 5751.234f the same process gives 5751.229999999999563.)
I would think carefully about why 15 decimals after the '.' is required, especially as the data only has 6-7 digits total precision -- so at most 6-7 "good" digits after the '.', depending on the size of the number.
FWIW: you could convert the number to scientific form, and then massage the string directly -- which is another way of doing what the code below does.
double pt[] = { 1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9,
1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19 } ;
double xx ;
int de ;
de = 6 - (int)ceilf(log10f(x)) ; /* where 'x' is the float to be shown */
if ((de > 15) || (de < -18)) /* de > 15 -- no need to round, value too small */
/* de < 18 -- cannot round, value too big */
xx = x ; /* xx is value to output */
else
{
while (1)
{
xx = x ;
if (de < 0)
xx /= pt[-de] ;
else if (de > 0)
xx *= pt[+de] ;
xx = round(xx) ;
if (xx < pt[5])
de += 1 ;
else if (xx > pt[6])
de -= 1 ;
else
break ;
} ;
if (de < 0)
xx *= pt[-de] ;
else if (de > 0)
xx /= pt[+de] ;
} ;
ss << std::fixed << std::setprecision(15) << xx ;
Upvotes: 2
Reputation: 180145
You can break it with your code. PC's work in binary, and 0.575 is a decimal fraction which is necessarily approximated.
The correct solution is to print your float to no more digits (7) than it physically stores, and to print its true value instead of what decimal value you think it should contain.
Upvotes: 2