问题
I have a NSMutableArray
which have some NSDecimalNumber
in it, like (500,50.80,70,8000)
Now I want to add all those decimal numbers
together.
I've tried to use
for (NSDecimalNumber *number in self.numbersArray)
{
NSDecimal *sum += [number decimalValue]
}
But failed.
回答1:
Use - (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber
Take a look at NSDecimalNumber Class Reference
NSDecimalNumber *lNumber = [NSDecimalNumber zero];
for (NSDecimalNumber *number in self.numbersArray)
{
lNumber = [lNumber decimalNumberByAdding:number];
}
回答2:
A simple way to add all NSNumber
s in an array is (similar to what @Mahonor said in a comment):
NSArray *myArray = ... // array of NSNumber (or NSDecimalNumber) objects
NSNumber *sum = [myArray valueForKeyPath:@"@sum.self"];
Contrary to what the Collection Operators: sum states, the numbers in the array are not converted to double
, but to NSDecimal
. Therefore, no precision is lost when adding decimal numbers. Even NSNumber
objects which are not decimal numbers are converted to NSDecimal
for the addition. The result of the summation is an instance of NSDecimalValue
.
I verified (or tried to) that in two different ways. First, I ran this code
NSNumber *a = [NSNumber numberWithDouble:1.2];
NSNumber *b = [NSDecimalNumber decimalNumberWithString:@"-5.7"];
NSArray *myArray = @[a, b];
id sum = [myArray valueForKeyPath:@"@sum.self"];
and activated Objective-C message logging by setting the environment variable "NSObjCMessageLoggingEnabled=YES". As can be seen in the created "/tmp/msgSends-NNNN" file, decimalNumber
(and not doubleValue
) is sent to both number objects.
Second, I created a custom class implementing both decimalValue
and doubleValue
, and applied @sum.self
to an array of objects of the custom class:
@interface MyClass : NSObject
@property (nonatomic, assign) double value;
@end
@implementation MyClass
- (NSDecimal)decimalValue
{
return [[NSNumber numberWithDouble:self.value] decimalValue];
}
- (double)doubleValue
{
return self.value;
}
@end
MyClass *a = [MyClass new]; a.value = 1.2;
MyClass *b = [MyClass new]; b.value = -5.7;
NSArray *myArray = @[a, b];
id sum = [myArray valueForKeyPath:@"@sum.self"];
By setting breakpoints in both methods, it is seen that only decimalValue
is used for the summation (and valueForKeyPath:@"@sum.self"
throws an exception if the class does not implement decimalValue
).
One can also see that decimalValue
is called from
-[NSArray(NSKeyValueCoding) _sumForKeyPath:]
and the assembler code for this method shows that NSDecimalAdd
is uses to add the numbers.
回答3:
Manohar's suggestion in the comments is not bad. You can indeed use KVC collection operators to make a one-liner out of this: [myArray valueForKeyPath:@"@sum.doubleValue"];
, but you potentially lose precision (depending on the numbers you have stored).
You're basically looking for "reduce" functionality; you need to chain calls to decimalNumberByAdding:
so that each call has the succeeding element of the array as its argument. Doing this on an NSArray
is easy enough, using performSelector:withObject:
@implementation NSArray (Reduce)
- (id)reduceUsingSelector: (SEL)sel
{
id res = [self objectAtIndex:0];
for( id obj in [self subarrayWithRange:(NSRange){1, [self count]-1}] ){
res = [res performSelector:sel withObject:obj];
}
return res;
}
@end
Use this like so: NSDecimalNumber * sum = [myArray reduceUsingSelector:@selector(decimalNumberByAdding:)];
The code you have isn't successful because NSDecimal is a struct, not an object; it shouldn't be declared as a pointer, and if it wasn't, you wouldn't be able to add it. That's not the right route to a solution.
来源:https://stackoverflow.com/questions/15378633/how-to-add-all-decimal-numbers-in-an-nsmutablearray