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.

有帮助吗?

解决方案 2

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

其他提示

A simple way to add all NSNumbers 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.

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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top