Question

The output of the following two lines seem to show that NSString's stringWithFormat statement does not round numbers consistently. It seems to round up correctly from 0.75 to 0.8. But it rounds 0.25 down to 0.2. Here is the code.

    NSString *sRoundedScore = [NSString stringWithFormat:@"%.1f", fScore];
    NSLog(@"\r\n score before rounding: %f, rounded score: %@", fScore, sRoundedScore);

When fScore is assigned to be 0.25, the output is:

score before rounding: 0.250000, rounded score: 0.2

When fScore is assigned to be 0.75, the output is:

score before rounding: 0.750000, rounded score: 0.8

I had to begin using NSNumberFormatter to get this to round consistently up. Why though does stringWithFormat produce this variation in results?

Was it helpful?

Solution

This answer goes back to "printf" functionality

Bankers Rounding:

It's important to know this is more stating that you want to show one decimal place on a Floating point integer.

Since you are stating one decimal point it is examing the second decimal to decide where to round. The rounding logic for %.f format'er works like so: if your number directly ahead is under 4 then 5 rounds down if you your number directly ahead is 4 and above 5 rounds up.

Reason of this: http://linuxgazette.net/144/misc/lg/a_question_of_rounding_in_issue_143.html: "For the GNU C library, the rounding rule used by printf() is "bankers rounding" or "round to even". This is more correct than some other C libraries, as the C99 specification says that conversion to decimal should use the currently selected IEEE rounding mode (default bankers rounding)."

found from this answer: printf rounding behavior for doubles

If you want to do a common rounding round inline above use the c function round() with a little math to do the trick or you can simply use NSNumberFormatter as you stated.

Rounding numbers in Objective-C

OTHER TIPS

The root problem is that when you see 0.25, the number is actually something more like 0.2499999999999. There are some standard techniques to get the rounding you want. In this particular case, you can multiply the float by 100.0f, then use lroundf, and print the long:

float foo = 0.25f;
NSLog(@"First %f", foo);
NSLog(@"Round %.1f", foo);
foo += 0.0000001;
NSLog(@"Second %f", foo);
NSLog(@"Round %.1f", foo);
//2013-07-18 15:36:09.021 EmailAddressFinder[12618:303] First 0.250000
//2013-07-18 15:36:09.022 EmailAddressFinder[12618:303] First 0.2
//2013-07-18 15:36:09.022 EmailAddressFinder[12618:303] Second 0.250000
//2013-07-18 15:36:09.022 EmailAddressFinder[12618:303] Second 0.3

foo = 0.25f;
foo *=100;

long l = lroundf(foo);
NSLog(@"l=%ld", l);
NSLog(@"0.%0.2ld", l); // .2=="print two places", 0=="add a leading 0 if needed"
//2013-07-18 15:37:24.424 EmailAddressFinder[13474:303] 0.25

You will need to adjust for ranges etc. Whenever I use US currency, I alway round to integral cents this way, and use a small method to give me a dollar.cents string back from just cents, by using this same technique.

EDIT: I see you awarded an answer - anyone who uses the behavior of printf to determine what their user is going to see is going to have problems at some point. Cooerce the numbers into something you know for sure and you will have robust and portable code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top