你是在正确的轨道上(有注意事项,见下文)。你应该只能使用来自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。
但是,我已经看到了关于堆栈溢出和围绕Web初始化NSDecimalNumber问题的很多讨论。 NSDecimalNumber
似乎存在一些与精度和双倍转换有关的问题。尤其是当您使用从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
所以你可以看到,使用numberWithDouble
方便的方法当NSNumber
(你真的不应该使用由于它返回错误的指针类型),甚至是初始化程序initWithDouble
(IMO“应该”可以调用),您将得到一个带有内部表示的NSDecimalNumber(通过在obj上调用description
返回ect)不如您在调用decimalNumberWithString:
或decimalNumberWithMantissa:exponent:isNegative:
时得到的结果准确。
另外,请注意,通过调用NSDecimalNumber
实例上的doubleValue
转换回双精度将失去精度,但有趣的是,无论您调用哪个初始化程序,都会相同。
所以最后,我认为在处理高精度浮点数时,建议您使用NSDecimalNumber
类级声明的decimalNumberWith*
方法之一来创建NSDecimalNumber实例。
那么当你有双精度,浮点数或其他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);
但你可以从输出中看到它下面的LOP掉一些不太显著数字:
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
所以从double
或float
到NSDecimalNumber最一贯准确转换/初始化是使用decimalNumberWithString:
。但是,正如布拉德拉森在他的回答中指出的那样,这可能会有点慢。如果性能成为问题,他使用NSScanner进行转换的技术可能会更好。
记住它是类方法'alloc'用于确定对象不是在init *方法的类型。 initWithFloat不返回一个NSNumber。你在alloc返回的NSDecimalNumber实例上调用initWithFloat。 – orj 2011-03-14 22:00:07
@orj - 是的,但是当我评论你的答案时,这有效地将NSNumber作为NSDecimalNumber进行类型化,这是不推荐的:http://www.cocoabuilder.com/archive/cocoa/227178-question-on-estimating -arraywithcapacity.html#227253。我在下面的评论中指出了一个失败的案例。 – 2011-03-14 23:32:26