How can I get NSDecimal to round to the nearest integer value? - ios

I am using NSDecimal because I have to store extremely large values for my application. I would like to be able to use the NSDecimalDivide function to divide two NSDecimals and round the result to the nearest integer.
NSDecimal testOne = [[NSDecimalNumber decimalNumberWithString:#"1064"] decimalValue];
NSDecimal cost = [[NSDecimalNumber decimalNumberWithString:#"10"]decimalValue];
NSDecimal value;
NSDecimalDivide(&value, &testOne, &cost, NSRoundDown);
NSString *string = NSDecimalString(&value, _usLocale);
NSLog(#"Here is the cost%#",string);
This out puts 106.4 I would like it to output 106. Does anyone know how to round a NSDecimal number to the integer value?

Wrap the NSDecimal up as an NSDecimalNumber and use decimalNumberByRoundingAccordingToBehavior:. This lets you specify the exact rounding behavior you desire.

One possible way to do that is using a NSDecimalNumberHandler with scale set to 0.
NSDecimalNumber *number1 = [NSDecimalNumber decimalNumberWithString:#"1064"];
NSDecimalNumber *number2 = [NSDecimalNumber decimalNumberWithString:#"10"];
NSDecimalNumberHandler *behavior = [[NSDecimalNumberHandler alloc] initWithRoundingMode:NSRoundDown
scale:0
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
NSDecimalNumber *result = [number1 decimalNumberByDividingBy:number2 withBehavior:behavior];
NSLog(#"Here is the cost %#", result);
Note that I am not using the primitive NSDecimal at all and you I would advise you the same.
However, if you want to work with NSDecimal directly, you can use the handy NSDecimalRound function.

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

How to efficiently convert a NSInteger into a NSDecimalNumber

So I want to do NSInteger -> NSDecimalNumber for decimal computation purpose.
However, the way I am currently doing it, is using NSNumber as an intermediate.
NSInteger -> NSNumber -> NSDecimalNumber
NSInteger myInteger = 41234312;
NSDecimalNumber *myDecimal = [[NSNumber numberWithInteger:myInteger] decimalValue];
Is there a more efficient way to perform this conversion?
You don't need to convert it to NSNumber, You can use :
NSDecimalNumber *myDecimal = [NSDecimalNumber numberWithInteger:myInteger];
NSDecimal number is a subclass of NSNumber so you can directly use this method.

How to divide NSDecimalNumber by integer?

I don't think a comprehensive, basic answer to this exists here yet, and googling didn't help.
Task: Given an NSDecimalNumber divide this by an int and return another NSDecimalNumber.
Clarification: amount_text below must be converted to a NSDecimalNumber because it is a currency. The result must be a NSDecimalNumber, but I don't care what format the divisor is.
What I have so far:
// Inputs
NSString *amount_text = #"15.3";
int n = 10;
NSDecimalNumber *total = [NSDecimalNumber decimalNumberWithString:amount_text];
// Take int, convert to string. Take string, convert to NSDecimalNumber.
NSString *int_string = [NSString stringWithFormat:#"%d", n];
NSDecimalNumber *divisor = [NSDecimalNumber decimalNumberWithString:int_string];
NSDecimalNumber *contribution = [total decimalNumberByDividingBy:divisor];
Surely, this can be done in a more straightforward way?
Is there any reason why you're using NSDecimalNumber? This can be done way easier like this:
// Inputs
NSString *amount_text = #"15.3";
int n = 10;
float amount = [amount_text floatValue];
float result = amount / n;
If you really want to do it with NSDecimalNumber:
// Inputs
NSString *amount_text = #"15.3";
int n = 10;
NSDecimalNumber *total = [NSDecimalNumber decimalNumberWithString:amount_text];
NSDecimalNumber *divisor = [NSDecimalNumber decimalNumberWithMantissa:n exponent:0 isNegative:NO];
NSDecimalNumber *contribution = [total decimalNumberByDividingBy:divisor];
You can always use initialisers when creating NSDecimalNumber. Since it is a subclass of NSNumber, NSDecimalNumber overrides initialisers.
So you can do
NSDecimalNumber *decimalNumber = [[NSDecimalNumber alloc] initWithInt:10];
however, you should be careful if your are doing high precision calculations as there are some problems using these initialisers. You can read about it here in more detail.
Alternatively (potentially losing some precision):
double amount = 15.3;
double n = 10.0;
double contribution = amount / n;
// conversion to decimal
NSString *string = [NSString stringWithFormat:#"%f", n];
NSDecimalNumber *contribution_dec = [NSDecimalNumber decimalNumberWithString:string];
better yet (if n=10):
[dec decimalNumberByMultiplyingByPowerOf10:-1];
As per your code...
You are creating an NSDecimalNumber from string and then doing manipulations with it.
I never do that. Unless you need NSDecimalNumber unless you want a boxed Objective-C Object, Avoid it, use float and double.
If you want to do it much simpler you can do it as:
float quotient = [total floatValue]/n;
or,
float quotient = [contribution floatValue]/n;
EDIT: If you want with any specific reason to use boxed type then you can use as:
NSString *amount_text = #"15.3";
int n = 10;
NSDecimalNumber *total = [NSDecimalNumber decimalNumberWithString:amount_text];
NSDecimalNumber *divisor = [[NSDecimalNumber alloc] initWithInt:n];
NSDecimalNumber *contribution = [total decimalNumberByDividingBy:divisor];

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];

Isn't NSDecimalNumber supposed to be able to do base-10 arithmetic?

NSDecimalNumber *minVal = [NSDecimalNumber decimalNumberWithString:#"0.0"];
NSDecimalNumber *maxVal = [NSDecimalNumber decimalNumberWithString:#"111.1"];
NSDecimalNumber *valRange = [maxVal decimalNumberBySubtracting:minVal];
CGFloat floatRange = [valRange floatValue];
NSLog(#"%f", floatRange); //prints 111.099998
Isn't NSDecimalNumber supposed to be able to do base-10 arithmetic correctly?
Yes, NSDecimalNumber operates in base-10, but CGFloat doesn't.
Yes, NSDecimalNumber is base-10.
Converting to a floating point type will can loose accuracy. In their case since the example just used NSLog just NSLog the NSDecimalNumber:
NSDecimalNumber *minVal = [NSDecimalNumber decimalNumberWithString:#"0.0"];
NSDecimalNumber *maxVal = [NSDecimalNumber decimalNumberWithString:#"111.1"];
NSDecimalNumber *valRange = [maxVal decimalNumberBySubtracting:minVal];
CGFloat floatRange = [valRange floatValue];
NSLog(#"floatRange: %f", floatRange);
NSLog(#"valRange: %#", valRange);
NSLog output:
floatRange: 111.099998
valRange: 111.1
OK, just doing that CGFloat aNumber = 111.1; shows as 111.099998 in the debugger, even before any operation has been performed on it. Therefore the precision is lost right when it is assigned to the less precise data type, regardless of any arithmetic operations occurring later.

Resources