NSDecimal、NSDecimalNumber、CFNumber 之间的正确选择是啥?
Posted
技术标签:
【中文标题】NSDecimal、NSDecimalNumber、CFNumber 之间的正确选择是啥?【英文标题】:What is the right choice between NSDecimal, NSDecimalNumber, CFNumber?NSDecimal、NSDecimalNumber、CFNumber 之间的正确选择是什么? 【发布时间】:2009-11-09 22:50:21 【问题描述】:我已经阅读了很多关于 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
应该返回一个对象。我在 comp.lang 上的 Objective-C 常见问题解答中阅读了有关 NSInvocation
和相关部分的内容。
我也明白我应该使用NSDecimalNumber
,但有两件事我想知道:
-
对于更复杂的类(只有这种财务计算,在 UITableView 中显示)是否可以接受内存开销和性能损失?我没有太多的 C 语言背景...
使用
decimalNumberByAdding:
这样的函数是不是太挑剔和复杂了?使用 Python 很容易定义 __add__
以将运算符与对象一起使用。我是否应该从NSDecimalNumber
获取浮点值,然后进行计算并返回包装在NSDecimalNumber
中的结果?您将如何处理这个问题?
我正在寻找一个简单而漂亮的解决方案!
同一领域的另一个问题:CFBoolean
是 iPhone Core Foundation 上BOOL
的对象包装器吗?
非常感谢您的帮助!
【问题讨论】:
【参考方案1】:如果您正在处理金融计算,您确实应该使用以 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
语法。
【讨论】:
感谢您的回答,我实际上需要从字符串创建选择器。 CFBolean 是包装 BOOL 的正确对象吗? 在 Cocoa 中,您应该使用 NSNumber 将 BOOL 包装在对象中。见+[NSNumber numberWithBool:]
。【参考方案2】:
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。
【讨论】:
感谢 Brad 的基准测试,我已经看过并觉得非常有趣。我计划使用 NSDecimalNumber,因为我发现它们更易于使用,并且我将在不久的将来使用 Core Data。顺便说一句,我不需要你的图形库那么快! 从浮点数或双精度数创建 NSDecimal 的最简单方法是什么? @Paul - 查看 Core Plot 框架的 CPUtilities.m 文件,了解一些可以进行各种转换的辅助函数:code.google.com/p/core-plot/source/browse/framework/Source/…。就个人而言,我已经开始使用格式字符串创建 NSDecimalNumber 实例,然后从中获取decimalValue
,因为我对 Core Plot 例程中进行的从 double 到 NSNumber 到 NSDecimal 的转换有点偏执。跨度>
请记住,如果您想存储这些数据并使用 Core Data,尽管性能差异,NSDecimalNumber 是正确的选择。【参考方案3】:
我想用它们的名字作为字符串来访问这些方法和设置器,因为我计划有很多像这样的其他类,我只保留一个字段列表来进行键值编码。
r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")];
KVC 不仅仅是使用字符串向对象发送消息。见the Key-Value Coding Programming Guide。
我是否应该从 NSDecimalNumber 获取浮点值,然后进行计算并返回包装在 NSDecimalNumber 中的结果?
没有。从十进制浮点 (NSDecimal
/NSDecimalNumber) 转换为二进制浮点 (float
/double
) 是有损的。如果你这样做,你的结果对于某些计算是不正确的。
【讨论】:
以上是关于NSDecimal、NSDecimalNumber、CFNumber 之间的正确选择是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Swift 3 十进制、NSDecimal 和 NSDecimalNumber
NSDecimalNumber 用于 Swift 中的货币精度 [重复]