mazziek
mazziek

Reputation: 11

NSNumber is not the same as its floatValue

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

Answers (1)

Reinhard Männer
Reinhard Männer

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

Related Questions