NSNumber is not the same as its floatValue - ios

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?

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.

Related

Round an NSString containing a decimal to two decimal digits

Given the NSString "1.625", I want to round this to "1.63".
How in the world do I do that?
This is what i have now:
NSString *rateString = [NSString stringWithFormat:#"%.2f", [#"1.63" doubleValue]];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
item.rate = [f numberFromString:rateString];;
However, doubleValue converts 1.625 to 1.6249999999
So when I round it to two decimal digits with #"%.2f", I end up with 1.62!
If you wanna round to the nearest hundredths, multiply by 100, increment by .5 and divide by 100. Then get the floor of that value.
double rate = [#"1.625" doubleValue];
double roundedNumber = floor(rate * 100 + 0.5) / 100;
NSString *rateString = [NSString stringWithFormat:#"%.2f", roundedNumber];
NSLog(#"rate: %#", rateString);
Running this then outputting the result:
2015-01-13 15:41:08.702 Sandbox[22027:883332] rate: 1.63
If you need high precision what you really need is NSDecimalNumberclass maybe coupled with NSDecimalNumberHandler if don't need to configure all details, or NSDecimalNumberBehaviors if need absolute control. This is the quickest solution to keep 2 decimal digits (the 'scale' value in handler init):
NSDecimalNumberHandler *handler = [[NSDecimalNumberHandler alloc]initWithRoundingMode:NSRoundBankers
scale:2
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:NO];
[NSDecimalNumber setDefaultBehavior:handler];
NSString *string = #"1.63";
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:string];
NSDecimalNumber docs:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDecimalNumber_Class/index.html
NSDecimalNumberHandler docs:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDecimalNumberHandler_Class/index.html
NSDecimalNumberBehaviors docs:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSDecimalNumberBehaviors_Protocol/index.html

Using NSNumberFormatter to Display Extremely Large Numbers

I'm working on a calculator app for the iPhone.
I've encountered an issue using NSNumberFormatter when displaying results of calculations that are greater than 15 digits.
For example, the result of the calculation 111,111,111,111,111 x 2 = 222,222,222,222,222 (correct). However, the result of the calculation 1,111,111,111,111,111 x 2 = 2,222,222,222,220 (wrong!).
Is there a limit to how many digits that can be displayed using NSNumberFormatter, or can someone tell me why the calculation result is not displaying correctly??
Thanks in advance!
Sample code:
double absResult = fabs(_result);
NSLog(#"_result = %f", _result);
//_result is the result of the calculation that will be placed onto displayLabel below
// _result = 2,222,222,222,222,222 for the calculation 1,111,111,111,111,111 x 2
NSNumberFormatter *displayString = [[NSNumberFormatter alloc]init];
//** Adds zero before decimal point **
[displayString setMinimumIntegerDigits:1];
[displayString setUsesGroupingSeparator:YES];
[displayString setGroupingSeparator:#","];
[displayString setGroupingSize:3];
[displayString setMinimumFractionDigits:2];
[displayString setMaximumFractionDigits:2];
NSString *resultString = [displayString stringFromNumber:[NSNumber numberWithDouble: _result]];
self.displayLabel.text = resultString;
This has nothing to do with NSNumberFormatter's capabilities but only with the precision of double floating point numbers. The double type uses 8 bytes to store 53 bit of mantissa and 11 bit exponent. See the Wikipedia article.
The 53 bit mantissa is too small to exactly represent 222,222,222,222,222, so the double is set to the closest possible representation.
If you want more precision you should try NSDecimalNumber which is better suited for a calculator app anyway.

Is there any easy way to round a float with one digit number in objective c?

Yes. You are right. Of Course this is a duplicate question. Before flag my question, please continue reading below.
I want to round a float value, which is
56.6748939 to 56.7
56.45678 to 56.5
56.234589 to 56.2
Actually it can be any number of decimal precisions. But I want to round it to nearest value. (If it is greater than or equal to 5, then round up and if not, then round down).
I can do that with the below code.
float value = 56.68899
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc]init];
[numberFormatter setMaximumFractionDigits:1];
[numberFormatter setRoundingMode:NSNumberFormatterRoundUp];
NSString *roundedString = [numberFormatter stringFromNumber:[NSNumber numberWithFloat:value]];
NSNumber *roundedNumber = [NSNumber numberFromString:roundedString];
float roundedValue = [roundedNumber floatValue];
Above code looks like a long process. I have several numbers to round off. So this process is hard to convert a float value into NSNumber and to NSString and to NSNumber and to float.
Is there any other easy way to achieve what I asked ?
I still have a doubt in the above code. It says roundUp. So when it comes to roundDown, will it work?
Can't you simply multiply by 10, round the number, then divide by 10?
Try
CGFloat float1 = 56.6748939f;
CGFloat float2 = 56.45678f;
NSLog(#"%.1f %.1f",float1,float2);
56.7 56.5
EDIT :
float value = 56.6748939f;
NSString *floatString = [NSString stringWithFormat:#"%.1f",floatValue];
float roundedValue = [floatString floatValue];
NSString* strr=[NSString stringWithFormat: #"%.1f", 3.666666];
NSLog(#"output is: %#",strr);
output is:3.7
float fCost = [strr floatValue];
This works for me
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:1];
[formatter setMinimumFractionDigits:0];
CGFloat firstnumber = 56.6748939;
NSString *result1 = [formatter stringFromNumber:[NSNumber numberWithFloat:firstnumber]];
NSLog(#"RESULT #1: %#",result1);
CGFloat secondnumber = 56.45678;
NSString *result2 = [formatter stringFromNumber:[NSNumber numberWithFloat:secondnumber]];
NSLog(#"RESULT #2: %#",result2);
CGFloat thirdnumber = 56.234589;
NSString *result3 = [formatter stringFromNumber:[NSNumber numberWithFloat:thirdnumber]];
NSLog(#"RESULT #2: %#",result3);
You don't want float, because that only gives you six or seven digits precision. You also don't want CGFloat, because that only gives you six or seven digits precision except on an iPad Air or iPhone 5s. You want to use double.
Rounding to one digit is done very simply:
double x = 56.6748939;
double rounded = round (10 * x) / 10;
You can use
[dictionaryTemp setObject:[NSString stringWithFormat:#"%.1f",averageRatingOfAllOrders] forKey:#"AvgRating"];
%.1f will give us value 2.1 only one digit after decimal point.
Try this :
This will round to any value not limited by powers of 10.
extension Double {
func roundToNearestValue(value: Double) -> Double {
let remainder = self % value
let shouldRoundUp = remainder >= value/2 ? true : false
let multiple = floor(self / value)
let returnValue = !shouldRoundUp ? value * multiple : value * multiple + value
return returnValue
}
}

NSNumber to float Value

When I convert NSNumber to float value using 'floatValue', there is a difference in precision. Example, I have a NSNumber 'myNumber' having value 2.3, and if I convert myNumber to float using 'floatValue', its value becomes, 2.29999. But I need exactly 2.30000. There is no problem with number of zeros after 2.3, I need '2.3' instead of '2.9'.
How can I do so?
I had similar situation where I was reading value and assigning it back to float variable again.
My Problem statement:
NSString *value = #"553637.90";
NSNumber *num = #([value floatValue]); // 1. This is the problem. num is set to 553637.875000
NSNumberFormatter *decimalStyleFormatter = [[NSNumberFormatter alloc] init];
[decimalStyleFormatter setMaximumFractionDigits:2];
NSString *resultString = [decimalStyleFormatter stringFromNumber:num]; // 2. string is assigned with rounded value like 553637.88
float originalValue = [resultString floatValue]; // 3. Hence, originalValue turns out to be 553637.88 which wrong.
Following worked for me after changing lines:
NSNumber *num = #([value doubleValue]); // 4. doubleValue preserves value 553637.9
double originalvalue = [resultString doubleValue]; // 5. While reading back, assign to variable of type double, in this case 'originalValue'
I hope this would be helpful. :)
If you need exact precision, don't use float. Use a double if you need better precision. That still won't be exact. You could multiply myNumber by 10, convert to an unsigned int and perform your arithmetic on it, convert back to a float or double and divide by 10 and the end result might be more precise. If none of these are sufficiently precise, you might want to look into an arbitrary precision arithmetic library such as GNU MP Bignum.
I've done the following but it is showing me correctly
NSNumber *num = [NSNumber numberWithFloat:2.3];
float f = [num floatValue];
NSLog(#"%f", f);
You can play with something like this:
float x = 2.3f;
NSNumber *n = [NSNumber numberWithFloat:x];
NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
[fmt setPositiveFormat:#"0.#"];
NSString *s = [fmt stringFromNumber:n];
float f = [s floatValue];

Convert NSString to currency format

In Java we do this statement to have a $ currency format.
double num1 = 3.99 ;
double num2 = 1.00 ;
double total = num1 + num2;
System.out.printf ("Total: $ %.2f", total);
The result is:
Total: $4.99
//--------------------------------
Now in iOS how can I get same format if I have the following statement :
total.text = [NSString stringWithFormat:#"$ %d",([self.coursePriceLabel.text intValue])+([self.courseEPPLabel.text intValue])+10];
Note:
If I use doubleValue the output always is 0 .
You can do the same thing with NSString:
NSString *someString = [NSString stringWithFormat:#"$%.2lf", total];
Note that the format specifier is "%lf" rather than just "%f".
But that only works for US dollars. If you want to make your code more localizable, the right thing to do is to use a number formatter:
NSNumber *someNumber = [NSNumber numberWithDouble:total];
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *someString = [nf stringFromNumber:someNumber];
Of course, it won't do to display a value calculated in US dollars with a Euro symbol or something like that, so you'll either want to do all your calculations in the user's currency, or else convert to the user's currency before displaying. You may find NSValueTransformer helpful for that.

Resources