2008-11-25 150 views
15

我有一些特定於語言環境的字符串(例如,0.01或0.01)。我想將此字符串轉換爲NSDecimalNumber。從examples I've seen thus far on the interwebs,這是通過使用一個NSNumberFormatter一拉完成:從特定於語言環境的字符串中獲取NSDecimalNumber?

NSString *s = @"0.07"; 

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 
[formatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; 
[formatter setGeneratesDecimalNumbers:YES]; 
NSDecimalNumber *decimalNumber = [formatter numberFromString:s]; 

NSLog([decimalNumber stringValue]); // prints 0.07000000000000001 

我使用的是10.4模式(除了每個文檔被推薦,這也是在iPhone上唯一可用的模式),但指示我想要生成十進制數字的格式化程序。請注意,我簡化了我的示例(實際上我正在處理貨幣字符串)。不過,我顯然做了錯誤的事情,它返回一個值,說明浮點數的不精確性。

將區域設置特定的數字字符串轉換爲NSDecimalNumber的正確方法是什麼?

編輯:請注意,我的例子是爲了簡單。我問的問題也應該涉及到何時需要採用特定於區域的貨幣字符串並將其轉換爲NSDecimalNumber。此外,可以將其擴展爲特定於語言環境的百分比字符串,並將其轉換爲NSDecimalNumber。

回答

13

根據Boaz Stuller的回答,我爲這個問題記錄了一個蘋果的bug。在解決問題之前,以下是我決定採取的最佳方法。這些變通辦法只依賴於將十進制數四捨五入到適當的精度,這是一種簡單的方法,可以補充您現有的代碼(而不是從格式化程序切換到掃描程序)。

通用數字

從本質上講,我只是根據四捨五入的規則,對於我的情況是有意義的數量。所以,YMMV取決於你所支持的精度。

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 
[formatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; 
[formatter setGeneratesDecimalNumbers:TRUE]; 

NSString *s = @"0.07"; 

// Create your desired rounding behavior that is appropriate for your situation 
NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:2 raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE]; 

NSDecimalNumber *decimalNumber = [formatter numberFromString:s]; 
NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior]; 

NSLog([decimalNumber stringValue]); // prints 0.07000000000000001 
NSLog([roundedDecimalNumber stringValue]); // prints 0.07 

貨幣

處理貨幣(這是我試圖解決實際問題)只是在處理一般的數字略有變化。關鍵是舍入行爲的規模取決於地區貨幣使用的最大小數位數。

NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init]; 
[currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; 
[currencyFormatter setGeneratesDecimalNumbers:TRUE]; 
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; 

// Here is the key: use the maximum fractional digits of the currency as the scale 
int currencyScale = [currencyFormatter maximumFractionDigits]; 

NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:currencyScale raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE]; 

// image s is some locale specific currency string (eg, $0.07 or €0.07) 
NSDecimalNumber *decimalNumber = (NSDecimalNumber*)[currencyFormatter numberFromString:s]; 
NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior]; 

NSLog([decimalNumber stringValue]); // prints 0.07000000000000001 
NSLog([roundedDecimalNumber stringValue]); // prints 0.07 
+1

只是一個側面說明,你應該格式化器的數字指向NSDecimalNumber指針。即NSDecimalNumber * decimalNumber =(NSDecimalNumber *)[formatter numberFromString:s]; – 2012-06-21 14:40:01

3

這似乎工作:

NSString *s = @"0.07"; 

NSScanner* scanner = [NSScanner localizedScannerWithString:s]; 
NSDecimal decimal; 
[scanner scanDecimal:&decimal]; 
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithDecimal:decimal]; 

NSLog([decimalNumber stringValue]); // prints 0.07 

此外,文件對這個錯誤。這絕對不是您在那裏看到的正確行爲。

編輯:直到Apple修復此問題(然後每個潛在用戶更新到固定的OSX版本),您可能將不得不使用NSScanner推出自己的解析器,或接受輸入數字的'僅'雙精度。除非你打算在這個應用程序中擁有五角大樓預算,否則我會建議後者。實際上,雙打精確到小數點後14位,因此在任何少於1萬億美元的情況下,它們都不會低於一分錢。我必須爲一個項目編寫基於NSDateFormatter的自己的日期解析例程,而且我花了一個月時間來處理所有有趣的邊緣案例(例如瑞典如何只有一天的時間包含在它的長期日期中)。

+0

錯誤#6400434登錄。 感謝您的回覆。正如我提到的,我的例子被簡化了(我實際上是在處理貨幣)。所以,我不確定這個答案適用於這種情況。 – shek 2008-11-25 19:16:36

+0

Boaz Stuller - 請參閱我的答案,它說明了一個代碼片段,它可以很簡單地處理這個問題,而不需要使用自定義解析器/ NSScanner ......而且可以在五角大樓的預算下使用。 ;) – shek 2008-11-26 17:48:21

0

參見Best way to store currency values in C++

處理貨幣的最佳方法是使用一個整數值的貨幣的最小單位,即美分美元/歐元等您將避免代碼中與浮點相關的精度錯誤。

考慮到這一點,以解析包含貨幣值的字符串最好的辦法是做手工(與一個可配置的小數點字符)。在小數點處拆分字符串,並將第一個和第二個部分解析爲整數值。然後使用構建你的綜合價值。

+0

感謝您的回答,unwesen。你的意見是處理貨幣的一個很好的經驗法則(我已經在這樣做)。但是,不要像您所描述的那樣手動解析輸入貨幣字符串,請參閱我的答案中的代碼示例,該代碼示例更加優雅地處理此問題。 – shek 2008-11-26 17:39:04

18

年後:

+(NSDecimalNumber *)decimalNumberWithString:(NSString *)numericStringNSDecimalNumber

相關問題