Reputation: 11
I came up strange behaviour of NSNumber
.
basically my code gets json from API call:
id json_response = [NSJSONSerialization
JSONObjectWithData:data
options:NSJSONReadingMutableLeaves
error:&error];
and stored as NSDictionary
then I got NSNumber
from it and using NSNumberFormatter
to convert it to a string, but...
NSNumber * avgNumber = [[_tableDataArray objectAtIndex:index ]
objectForKey:kTrendsKeyAvgScoreFloat];
here is formatting output:
NSNumberFormatter * formatter = [[NSNumberFormatter alloc]init];
[formatter setDecimalSeparator:@","];
[formatter setMaximumFractionDigits:1];
[formatter setMinimumFractionDigits:0];
[formatter setGroupingSeparator:@"."];
[formatter setGroupingSize:3];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSString * retString = [formatter stringFromNumber:number];
I got strange behaviour when NSNumber
has value of 1.05
(__NSCFNumber *) avgNumber = 0x0000000155923800 (float)1.050000
But with code above it was printing (instead of expected 1.1):
1
However when I checked float value
float avg = [avgNumber floatValue];
it turns out that
Printing description of avg:
(double) avg = 1.0499999523162842
Even if I try to round it to 2 decimal points
avg = roundf(avg * 100); // here 105
avg = avg / 100; // and here is 1.0499999523162842 again
However if I test the code and put 1.05 manually inside NSDictionary
everything works as expected.
If someone could explain why is that? And how to preserve every time proper display?
Upvotes: 0
Views: 366
Reputation: 15217
The problem is that a value of 1.05 cannot be represented exactly by a floating point number. This is only possible for values that are sums of (positive or negative) powers of 2. One can, thus, exactly represent values like 1 (=2^0) or 1.03125 (= 2^0 + 2^-5), but the best approximation to 1.5 is in your case 1.0499999523162842.
Now when you initialize an NSNumberFormatter
, and do no set its roundingMode
property, its default value is kCFNumberFormatterRoundHalfEven
, which means the number will be rounded „towards the nearest integer, or towards an even number if equidistant.“
So if your number is 1.0499999523162842, it will be rounded to 1.0, and this is output as 1.
Upvotes: 4