views:

858

answers:

5

I'm using the following code for rounding to 2dp:

sprintf(temp,"%.2f",coef[i]); //coef[i] returns a double

It successfully rounds 6.666 to 6.67, but it doesn't work properly when rounding 5.555. It returns 5.55, whereas it should (at least in my opinion) return 5.56.

How can I get it to round up when the next digit is 5? i.e. return 5.56.

edit: I now realise that this is happening because when I enter 5.555 with cin it gets saved as 5.554999997.

I'm going to try rounding in two stages- first to 3dp and then to 2dp. any other (more elegant) ideas?

+5  A: 

It seems you have to use math round function for correct rounding.

printf("%.2lf %.2lf\n", 5.555, round(5.555 * 100.)/100.);

This gives the following output on my machine:

5.55 5.56

Note that you also need %lf for printing a double, not only %f.

ypnos
This seems a bit more reliable than the idea of adding 0.0005, but both would work I guess. Thanks for your help!
Dave
It seems to be a bug, really. 5.5551 gets rounded correctly!
ypnos
It's not buggy, @ypnos, you just don't yet grok the limits of floating point. 5.5551 is represented as 5.555100000000000370... which will round up, 5.555 is repesented as 5.5549999999... which will round down. You can see it's a representation issue by trying your code with 5.5500000000000000001 - both outputs are the incorrect 5.55 in that case because the number ISN'T 5.55000...1 when it's stored. It's 5.549999...
paxdiablo
I grok the limits of floating point quite well. The bug I see here is the discrepancy, while you may argue that it is an expected one (due to limitations of the format). And thank you for downvoting a CORRECT answer!
ypnos
I didn't downvote you @ypnos, it's not my style to do that to a "competing" answer - I'd rather let the greater community decide. In any case, there's nothing wrong with your answer at all, just with the comment you made afterwards - it's not a bug. The limitations of IEE754 are well understood and documented (by everyone except the hordes of people that still seem to ask questions about it on SO :-). It's only a bug if the actual behavior does not match documented behavior; that is NOT the case here.
paxdiablo
True. Thanks for your reply.
ypnos
In fact, here's an upvote for being so gracious (and because I couldn't fault your method, integers are *much* easier to play with than floats).
paxdiablo
+6  A: 

The number 5.555 cannot be represented as an exact number in IEEE754. Printing out the constant 5.555 with "%.50f" results in:

5.55499999999999971578290569595992565155029300000000

so it will be rounded down. Try using this instead:

printf ("%.2f\n",x+0.0005);

although you need to be careful of numbers that can be represented exactly, since they'll be rounded up wrongly by this expression.

You need to understand the limitations of floating point representations. If it's important that you get accuracy, you can use (or code) a BCD or other decimal class that doesn't have the shortcoming of IEEE754 representation.

paxdiablo
@paxdiablo: This doesn't affect your answer, but I was just curious: what language did you use to print 5.55499999999999971578290569595992565155029300000000? It actually should be 5.55499999999999971578290569595992565155029296875000
Rick Regan
@Rick, most likely gcc under CygWin but it's hard to remember from a year ago :-) I get the same result as you under Ubuntu10. But I think doubles only guarantee 15 decimal digits of accuracy anyway so it may be using a wider internal format (I have a vague recollection that calculations on at least one system used 80bits internally but that would have been a long time ago - to get 48 decimal digits as with your number, you'd need about 160 bits of precision).
paxdiablo
@paxdiablo: OK, thanks. I knew it couldn't be Visual C++ (it can't print that many digits accurately) and it seemed to have too few digits for gcc/glibc on Linux (which allows you to print them all). In any case, you can get that many digits with a standard double (53 bits) -- it's the exact decimal representation of what the double holds (even if it may only be a 15 digit approximation of what was assigned to it).
Rick Regan
A: 

You could also do this (saves multiply/divide):

printf("%.2f\n", coef[i] + 0.00049999);
cantabilesoftware
This will round 5.549999 to 5.56 ;-)
ypnos
@ypnos, you really need to check things before you post - it does NOT give you 5.56 at all.
paxdiablo
Sorry misconception. You should still see my point. 5.544999 + 0.0005 is rounded to 5.55. It should be clearly rounded to 5.54. Doesn't take rocket science to realize that adding a bias will not mysterically fix a rounding problem without introducing new ones.
ypnos
One of the few answers I'll downvote. This is completely wrong.
John Dibling
A: 

This question is tagged C++, so I'll proceed under that assumption. Note that the C++ streams will round, unlike the C printf family. All you have to do is provide the precision you want and the streams library will round for you. I'm just throwing that out there in case you don't already have a reason not to use streams.

Brian Neal
+1  A: 

How about this for another possible solution:

printf("%.2f", _nextafter(n, n*2));

The idea is to increase the number away from zero (the n*2 gets the sign right) by the smallest possible amount representable by floating point math.

Eg:

double n=5.555;
printf("%.2f\n", n);
printf("%.2f\n", _nextafter(n, n*2));
printf("%.20f\n", n);
printf("%.20f\n", _nextafter(n, n*2));

With MSVC yields:

5.55
5.56
5.55499999999999970000
5.55500000000000060000
cantabilesoftware