开发者

How to perform an accurate currency calculation in an iPhone app

开发者 https://www.devze.com 2022-12-13 10:40 出处:网络
I\'ve written an iPhone App that calculates a total cost by adding/subtracting units of predetermined cost. This was working fine until I realised that the float values I was using caused the total to

I've written an iPhone App that calculates a total cost by adding/subtracting units of predetermined cost. This was working fine until I realised that the float values I was using caused the total to be inaccurate so I started to look into using NSDecimalNumber. I'm now rather confused and have an 'out of scope' error. Relevant sections of my (simplified) code are below. How can I get this to work as expected?

MyViewController.h

#import <UIKit/UIKit.h>

@interface MyViewController : UIViewController {
    IBOutlet UILabel *totalPrice;
    NSDecimalNumber *total;
    NSDecimalNumber *item1Price;
}

@property (retain, nonatomic) NSDecimalNumber *total;
@property (retain, nonatomic) UILabel *totalPrice;

- (IBAction)addItem:(id)sender;
- (void)getDefaults;
- (void)setLabels;
@end

MyViewController.m

#import "MyViewController.h"

@implementation MyViewController

@synthesize total;
@synthesize totalPrice;

- (void)getDefaults {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"prices" ofType:@"plist"];
    NSDictionary *dict = [[NSDictionary alloc] initWithContentsO开发者_开发知识库fFile:path];
    total = [NSDecimalNumber decimalNumberWithString:@"0"];
    item1Price = (NSDecimalNumber *)[dict valueForKey:@"item1"];
}

- (void)setLabels {
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [numberFormatter setCurrencySymbol:@"£"];
    [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    totalPrice.text = [numberFormatter stringFromNumber:total];
}

- (void)viewDidLoad {
    [self getDefaults];
    [self setLabels];
}

- (IBAction)addItem:(id)sender {
    NSDecimalNumber *itemPrice;

    switch ([sender tag]) {
        case 1:
            itemPrice = item1Price;
            break;
    }

    total = [total decimalNumberByAdding:itemPrice];

    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [numberFormatter setCurrencySymbol:@"£"];
    [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

    totalPrice.text = [numberFormatter stringFromNumber:total];
}

@end

I'm hoping that I'm on the right path...


You could use integers to do the calculation, just multiply out the fractional part and divide to get back to the correct value.

Otherwise you may want to try a double, that's what is actually used for most of the SDK data types that require precision(ie: CLLocationCoordinate, NSTimeInterval).



Your out of scope error could be coming from the fact that 'total' and 'itemPrice1' are defined as an instance variable and are used as such, i.e. you use them across the class. The getDefaults method sets their values, which are then used in other methods. However, by initialising these variable as:

 total = [NSDecimalNumber decimalNumberWithString:@"0"];
item1Price = (NSDecimalNumber *)[dict valueForKey:@"item1"];

...they autorelease at the end of that method (in that scope). You have two options to initialise:

total = [[NSDecimalNumber decimalNumberWithString:@"0"] retain];
// OR
total = [[NSNumber alloc] initWithString:@"0"];

No matter what option you choose from the above two, you will need to release later (usually in the dealloc method of the class).

Not sure what the problem was using NSNumber. You mentioned float not being accurate enough, in which case I would just use a double instead. You could do this:

double totalSetter = 0.0;
NSNumber *total = [[NSNumber alloc] initWithDouble:totalCaster];

and then retrieve using:

double totalGetter = [total doubleValue];

Now you should be able to use the NSNumberFormatter with the NSNumbers you've made.


There were two issues here. One was the scope error, which I resolved using retain as suggested by imnk. I'd already tried this out but my decimalNumberByAdding was giving inaccurate results.

It turns out that despite setting itemPrice1 up as a NSDecimalNumber the line:

item1Price = (NSDecimalNumber *)[dict valueForKey:@"item1"];

was changing it to a NSCFNumber. To solve this I changed this variable to an NSString and created a NSDecimalNumber from the string to use it in my method.

0

精彩评论

暂无评论...
验证码 换一张
取 消