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 中的货币精度 [重复]

如何声明 NSDecimal 以及如何使用 NSDecimalAdd

NSDecimal 需要桥接

Swift - 将两个 NSDecimal 数字相乘

将非常大的 NSDecimal 转换为字符串,例如。 400,000,000,000 -> 400 T 等等