你是在正確的軌道上(有注意事項,見下文)。你應該只能使用來自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