0

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.

2
  • 1
    If you need to use float, you can't expect more than 7 (or so) decimal digits of precision. If you need more digits, you'll have to put up with the cost of using double (or perhaps some user-defined decimal type, to represent your values exactly). I'm afraid there's no way to get more precision out of a float. Commented Aug 22, 2014 at 11:17
  • Is saving in binary an option? Commented Aug 24, 2014 at 5:52

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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 ; 

11 Comments

Thank you for the explanation. I have tried your code and it gives me the idea how to convert it. I want to do some test about speed of the code with regard to string. It is working good when decimal places is zero and positive numbers. For example when I put x = 0.293751740273658f; it gives me 0.293752000000000. But if I add some decimals, x = 123.293751740273658f; it gives me 123.293999999999997. Also there is a problem with negative numbers. When I put x = -0.293751740273658f; it gives me -0.293751746416092
Yes, to handle -ve numbers you need deal with the sign. Yes, as I said, depending on the size of the number, when you ask for 15 digits after the decimal point, you will see the limits of double precision. Double precision gives you 15 (nearly 16) decimal digits total precision (significant figures) -- no matter where the decimal point is. There is no way around that. 123.752000000000000 has 18 decimal digits (significant figures). The question is not really how to do what you seem to want, but why do you want it ?!
I want to use double instead of float but my system (I have to use an old library) use float in one place. I want to convert float to double more correctly. But as I wrote, I don't need now, maybe I will have to use, this question is preparing to future.
Well, the problem is what we mean by "convert float to double more correctly". Ordinary conversion (eg assignment) preserves the float's 24 binary digits exactly. What I have suggested converts the float, but keeps only about 20 binary digits, by converting 6 significant decimal digits. But the question is not why do you want double, but why do you want to show 15 decimal digits after the point ?
I don't want to show 15 decimal digits after the point. I just write my code with 15. It can be 10, 13 or 17. It is not the point of the question. The question is how to convert more correctly then an assignment conversion and better than a string conversion.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.