我正在寻找从双精度或短精度中实例化NSDecimalNumber的最佳方法.有以下NSNumber类和实例方法...

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

但这些似乎返回了一个数字.另一方面,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页上找到.

然而,我在网上看到了很多关于堆栈溢出的讨论,以及关于初始化NSDecimalNumber的问题.似乎存在一些与精确度以及与NSDecimalNumber的双打之间的转换有关的问题.特别是当您使用从NSN成员处继承的号码时.

我完成了下面的测试:

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"应该"可以调用)上使用numberWithDouble便利方法时,您会得到一个NSDecimalNumber,其内部表示(通过调用对象上的description返回)不如当你调用decimalNumberWithString:decimalNumberWithMantissa:exponent:isNegative:时会得到一个.

另外,请注意,通过在NSDecimalNumber实例上调用doubleValue来转换回double会丢失精度,但有趣的是,无论调用哪个初始值设定器,转换都是一样的.

最后,我建议您在处理高精度浮点数时,使用NSDecimalNumber类级别声明的decimalNumberWith*个方法之一来创建NSDecimalNumber实例.

那么,当你有一个双精度、浮点数或其他数字时,如何轻松地调用这些初始值设定项呢?

两种方法描述了here个"工作",但仍然存在精度问题.

如果您已经将该号码存储为NSNumber,那么这should起作用:

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

如果数字是一个基本数(浮点或双精度),那么这should起作用:

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进行转换的技术可能会更好.

Objective-c相关问答推荐

react 本机WebView菜单项目props 无法在iOS&>=16上使用

如何删除Objective-C中未定义的符号&错误?

iOS - 如何预加载键盘?

在从 UIViewController 调用的非保留完成中引用 self 时,weakSelf/strongSelf 舞蹈真的有必要吗?

XCTAssertEqual 不适用于双精度值

如何可靠地检测 iOS 9 上是否连接了外部键盘?

performSelector 的返回值:

如何在旋转时定义 CollectionView 的大小

UILabel:你如何设置字体大小?

使用 [NSBundle mainBundle] pathForResource: ofType:inDirectory: 访问文件

如果安装了应用程序,如何以编程方式判断?

更改 UIDatePicker 字体 colored颜色 ?

类在两者中都实现.将使用两者之一

未找到 Apple Mach-O 链接器警告目录

dyld:找不到符号:try 运行 iOS 应用程序时的 _NSURLAuthenticationMethodClientCertificate

有没有办法更新单个 UITableViewCell 的高度,而无需重新计算每个单元格的高度?

如何用 Java 开发 iPhone 应用程序?

[NSMutableArray array] 与 [[NSMutableArray alloc] init] 之间的区别

为什么 detailTextLabel 不可见?

可以在 NSLocalizedString 中使用变量和/或参数吗?