Why does the NSString class round inconsistently when formatting numbers?

做~自己de王妃 提交于 2019-12-23 01:41:57

问题


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?


回答1:


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




回答2:


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.



来源:https://stackoverflow.com/questions/17725983/why-does-the-nsstring-class-round-inconsistently-when-formatting-numbers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!