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];
Related
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.
I have a value coming from an API server. The server stores it as BigDecimal on Postgres. On the iOS side, the value is of type __NSCFNumber. How do I get the absolute value for NSCFNumber?
My primary purpose is to further convert this number to a currency.
__NSCFNumber is an internal class that implements the NSNumber interface.
If you want a double:
NSNumber *myNumber = objectFromPostgres;
double value = fabs(myNumber.doubleValue);
If you want a long:
NSNumber *myNumber = objectFromPostgres;
long value = labs(myNumber.longValue);
If you want an int:
NSNumber *myNumber = objectFromPostgres;
int value = abs(myNumber.intValue);
You have a choice on the precision, but assuming a double value, you can use this:
NSNumber *number = [NSNumber numberWithDouble:-3.14159]; // isa Class __NSCFNumber 0x0000000101357a20
double absoluteValue = fabsl([number doubleValue]);
NSLog(#"absolute value = %f", absoluteValue);
Outputs:
absolute value = 3.141590
I asked an incomplete and vague question and I'm sorry. For my sins, I'm posting my solution and maybe it can help someone.
NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundUp
scale:2
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
NSDecimalNumber *totalAmount = [NSDecimalNumber decimalNumberWithDecimal:[user[#"total"] decimalValue]];
totalAmount = [[self abs:totalAmount] decimalNumberByRoundingAccordingToBehavior:roundUp];
The helper method that I got from this SO question.
- (NSDecimalNumber *)abs:(NSDecimalNumber *)num {
if ([num compare:[NSDecimalNumber zero]] == NSOrderedAscending) {
// Number is negative. Multiply by -1
NSDecimalNumber * negativeOne = [NSDecimalNumber decimalNumberWithMantissa:1
exponent:0
isNegative:YES];
return [num decimalNumberByMultiplyingBy:negativeOne];
} else {
return num;
}
}
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.
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];
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.