31

我已经阅读了很多关于 NSDecimal、NSNumber、NSNumberDecimal、CFNumber 的内容……对我来说它开始成为一种丛林。

基本上,我正在尝试创建一个简单的模型类来处理简单的计算,比如这个:

#import <Foundation/Foundation.h>


@interface Test : NSObject
{
    float rate;
    float amount;
    int duration;
}

- (float)capitalizedAmount;

@end

@implementation Test

- (float)capitalizedAmount {
    return (amount*pow((1.0+rate),duration));
}

@end

我想用它们的名字作为字符串来访问这些方法和设置器,因为我计划有很多像这样的其他类,而且我只保留一个字段列表来进行键值编码。

// This is just the desired behavior
// This evidently won't work with the previous class definition
Test *obj = [[Test alloc] init];
[NSNumber numberWithInt:10]
...
float r;
r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];

我知道这是不可能的,performSelector:它将返回一个对象,因此capitalizedAmount应该返回一个对象。我NSInvocation在 comp.lang 上的 Objective-C 常见问题解答中阅读了有关内容和相关部分。

我也知道我应该使用NSDecimalNumber,但有两件事我想知道:

  1. 对于更复杂的类(只有这种财务计算,在 UITableView 中显示),内存开销和性能损失是否可以接受?我在C方面没有太多背景...
  2. 使用类似的功能是不是太挑剔和复杂了decimalNumberByAdding:?使用 Python,很容易定义__add__对对象使用运算符。我应该从 获取浮点值NSDecimalNumber,然后进行计算并返回包含在 中的结果NSDecimalNumber吗?你会如何处理这个问题?

我正在寻找一个简单而漂亮的解决方案!

只是同一领域的另一个问题:CFBoolean对象包装器是否适用BOOL于 iPhone Core Foundation?

非常感谢您的帮助!

4

3 回答 3

28

如果您正在处理金融计算,您确实应该使用以 10 为底的算术来避免标准以 2 为底的浮点类型可能发生的舍入错误。所以它是 NSDecimal 或 NSDecimalNumber。由于您正在编写面向对象的代码,因此 NSDecimalNumber 是您的正确选择。

回答您的问题:只有测试您的代码才能揭示您是否可以接受内存开销和性能损失。我并没有真正使用 NSDecimalNumber,但我敢打赌 Apple 的实施非常有效,并且足以满足大多数人的需求。

decimalNumberByAdding:不幸的是,由于 Objective-C 不像 C++ 那样支持运算符重载,因此您将无法避免类似的情况。我同意这会使您的代码不那么优雅。

对您发布的代码的一条评论:r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];相当不雅。任何一个

r = [obj performSelector:@selector(capitalizedAmount)];

甚至是简单的

r = [obj capitalizedAmount];

NSSelectorFromString除非您出于其他原因需要语法,否则会更好。

于 2009-11-10T00:17:25.047 回答
20

Ole 是正确的,因为您应该使用 NSDecimal 或 NSDecimalNumber 来避免在进行财务计算时出现浮点数学错误。但是,我的建议是使用 NSDecimal 及其 C 函数,而不是 NSDecimalNumber。NSDecimal 计算可以更快,并且由于它们避免创建大量自动释放的对象,因此在内存使用方面要好得多。

例如,我在 MacBook Air 上对这两种类型的数学运算进行了基准测试:

NSDecimal

Additions per second: 3355476.75
Subtractions per second: 3866671.27
Multiplications per second: 3458770.51
Divisions per second: 276242.32

NSDecimalNumber

Additions per second: 676901.32
Subtractions per second: 671474.6
Multiplications per second: 720310.63
Divisions per second: 190249.33

在使用 NSDecimal 与 NSDecimalNumber 时,除法运算是唯一没有将性能提高大约五倍的运算。iPhone 上也有类似的性能改进。这与节省内存一起,是我们最近将 Core Plot 切换到使用 NSDecimal 的原因。

您将遇到的唯一困难是将值输入和输出 NSDecimal 类型。直接往返浮点和整数值可能需要使用 NSDecimalNumber 作为桥梁。此外,如果您使用 Core Data,您会将值存储为 NSDecimalNumbers,而不是 NSDecimals。

于 2009-11-10T18:48:09.157 回答
3

我想用它们的名字作为字符串来访问这些方法和设置器,因为我计划有很多像这样的其他类,而且我只保留一个字段列表来进行键值编码。

r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];

KVC 不仅仅是使用字符串向对象发送消息。请参阅键值编码编程指南

我是否应该从 NSDecimalNumber 获取浮点值,然后进行计算并返回包含在 NSDecimalNumber 中的结果?

否。从十进制浮点 ( /NSDecimalNumber)转换为二进制浮点( float/ ) 是有损的。如果您以这种方式进行某些计算,您的结果将是不正确的。doubleNSDecimal

于 2009-11-10T04:19:50.983 回答