iphone - 从 float 或 double 实例化 NSDecimalNumber 的正确方法

标签 iphone objective-c cocoa

我正在寻找从 double 或 short 实例化 NSDecimalNumber 的最佳方法。有以下 NSNumber 类和实例方法...

+NSNumber numberWithFloat
+NSNumber numberWithDouble
-NSNumber initWithFloat
-NSNumber initWithDouble

但这些似乎返回 NSNumber。另一方面,NSDecimalNumber 定义如下:

+NSDecimalNumber decimalNumberWithMantissa:exponent:isNegative:
+NSDecimalNumber decimalNumberWithDecimal:

这里有几种可能性。如果您将 NSDecimalNumber 设置为上述 NSNumber 便捷方法的返回值,Xcode 会生成警告。

希望您能就最干净和正确的方式提供意见...

最佳答案

你走在正确的轨道上(有一些警告,见下文)。您应该能够只使用来自 NSNumber 的初始化程序,因为它们是由 NSDecimalNumber 继承的。

NSDecimalNumber *floatDecimal = [[[NSDecimalNumber alloc] initWithFloat:42.13f] autorelease];
NSDecimalNumber *doubleDecimal = [[[NSDecimalNumber alloc] initWithDouble:53.1234] autorelease];
NSDecimalNumber *intDecimal = [[[NSDecimalNumber alloc] initWithInt:53] autorelease];

NSLog(@"floatDecimal floatValue=%6.3f", [floatDecimal floatValue]);
NSLog(@"doubleDecimal doubleValue=%6.3f", [doubleDecimal doubleValue]); 
NSLog(@"intDecimal intValue=%d", [intDecimal intValue]);

有关此主题的更多信息,请访问 here .

但是,我在 Stack Overflow 和网络上看到了很多关于初始化 NSDecimalNumber 问题的讨论。使用 NSDecimalNumber 似乎存在一些与精度和与 double 的转换有关的问题。尤其是当你使用继承自 NSNumber 的初始化成员时。

我敲了下面的测试:

double dbl = 36.76662445068359375000;
id xx1 = [NSDecimalNumber numberWithDouble: dbl]; // Don't do this
id xx2 = [[[NSDecimalNumber alloc] initWithDouble: dbl] autorelease];
id xx3 = [NSDecimalNumber decimalNumberWithString:@"36.76662445068359375000"];
id xx4 = [NSDecimalNumber decimalNumberWithMantissa:3676662445068359375L exponent:-17 isNegative:NO];

NSLog(@"raw doubleValue: %.20f,              %.17f", dbl, dbl);

NSLog(@"xx1 doubleValue: %.20f, description: %@", [xx1 doubleValue], xx1);   
NSLog(@"xx2 doubleValue: %.20f, description: %@", [xx2 doubleValue], xx2);   
NSLog(@"xx3 doubleValue: %.20f, description: %@", [xx3 doubleValue], xx3);   
NSLog(@"xx4 doubleValue: %.20f, description: %@", [xx4 doubleValue], xx4);   

输出是:

raw doubleValue: 36.76662445068359375000               36.76662445068359375
xx1 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
xx2 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
xx3 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
xx4 doubleValue: 36.76662445068357953915, description: 36.76662445068359375

所以你可以看到,当在 NSNumber 上使用 numberWithDouble 便捷方法时(你不应该真正使用它,因为它返回错误的指针类型),甚至初始化器 initWithDouble (IMO“应该”可以调用)你会得到一个 NSDecimalNumber ,其内部表示(通过在对象上调用 description 返回)不准确当您调用 decimalNumberWithString:decimalNumberWithMantissa:exponent:isNegative: 时得到的结果。

另外,请注意,通过在 NSDecimalNumber 实例上调用 doubleValue 转换回 double 会丢失精度,但有趣的是,无论您调用什么初始化程序,精度都是一样的。

所以最后,我认为建议您在处理时使用在 NSDecimalNumber 类级别声明的 decimalNumberWith* 方法之一来创建您的 NSDecimalNumber 实例高精度 float 。

那么当你有一个 double、float 或其他 NSNumber 时,你如何轻松地调用这些初始化器呢?

描述了两种方法here “工作”,但仍然存在精度问题。

如果您已经将号码存储为 NSNumber,那么这个应该可以工作:

id n = [NSNumber numberWithDouble:dbl];
id dn1 = [NSDecimalNumber decimalNumberWithDecimal:[n decimalValue]];

NSLog(@"  n doubleValue: %.20f, description: %@", [n doubleValue], n);   
NSLog(@"dn1 doubleValue: %.20f, description: %@", [dn1 doubleValue], dn1);  

但正如您从下面的输出中看到的那样,它去掉了一些不太重要的数字:

  n doubleValue: 36.76662445068359375000, description: 36.76662445068359
dn1 doubleValue: 36.76662445068357953915, description: 36.76662445068359

如果数字是原始数字( float 或 double ),那么 应该 工作:

id dn2 = [NSDecimalNumber decimalNumberWithMantissa:(dbl * pow(10, 17))
                                          exponent:-17 
                                        isNegative:(dbl < 0 ? YES : NO)];

NSLog(@"dn2 doubleValue: %.20f, description: %@", [dn2 doubleValue], dn2);

但是您会再次遇到精度错误。正如您在下面的输出中看到的:

dn2 doubleValue: 36.76662445068357953915, description: 36.76662445068359168

我认为这种精度损失的原因是由于涉及浮点乘法,因为以下代码可以正常工作:

id dn3 = [NSDecimalNumber decimalNumberWithMantissa:3676662445068359375L 
                                           exponent:-17 
                                         isNegative:NO];

NSLog(@"dn3 doubleValue: %.20f, description: %@", [dn3 doubleValue], dn3);   

输出:

dn3 doubleValue: 36.76662445068357953915, description: 36.76662445068359375

因此,从 doublefloat 到 NSDecimalNumber 的最一致准确的转换/初始化是使用 decimalNumberWithString:。但是,正如 Brad Larson 在他的回答中指出的那样,这可能有点慢。如果性能成为问题,他使用 NSScanner 进行转换的技术可能会更好。

关于iphone - 从 float 或 double 实例化 NSDecimalNumber 的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5304855/

相关文章:

ios - iOS 在 Web View 中加载 html 文件时屏幕黑屏

objective-c - 带有&符号的文本文件被绘制到电子邮件消息停止模板中

ios - 将 UISearchBar returnKeyType 更改为 Done 在 iPhone 5 上有效,但在 iPhone 4s 上会使应用程序崩溃

iphone - 一个 iPhone 应用程序的多个版本具有相同的显示名称

ios - CGAffineTransform 动画没有在正确的位置结束

objective-c - 从 Cocoa 中的字符串解析简单的 XML?

iphone - 替换数组中的字符串

cocoa - NSAttributedString/NSTextTab 对齐问题

iphone - 从属性列表中检索嵌套的 NSDictionary

iPhone - 带有双引号转义字符的 Localizable.strings