15

ロケール固有の string がいくつかあります (例: 0.01 または 0,01)。この文字列を NSDecimalNumber に変換したいと思います。これまでにインターウェブで見た例から、これは NSNumberFormatter a la を使用して実現されます。

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 で使用できる唯一のモードでもあります) が、10 進数を生成することをフォーマッターに示しています。例を単純化したことに注意してください (実際には通貨文字列を扱っています)。ただし、浮動小数点数の不正確さを示す値を返すには、明らかに何か間違っています。

ロケール固有の数値文字列を NSDecimalNumber に変換する正しい方法は何ですか?

編集:私の例は簡単にするためのものであることに注意してください。私が尋ねている質問は、ロケール固有の通貨文字列を取り、それを NSDecimalNumber に変換する必要がある場合にも関連するはずです。さらに、これをロケール固有のパーセンテージ文字列に展開して、NSDecimalNumber に変換することもできます。

4

4 に答える 4

18

数年後:

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

于 2011-05-20T18:34:45.927 に答える
13

Boaz Stuller の回答に基づいて、この問題について Apple にバグを記録しました。それが解決されるまでの間、最善の方法であると私が判断した回避策を以下に示します。これらの回避策は、(フォーマッターからスキャナーに切り替えるのではなく) 既存のコードを補完できる単純なアプローチである、適切な精度に 10 進数を丸めることに依存しています。

一般番号

基本的に、私は自分の状況に適したルールに基づいて数値を丸めているだけです。したがって、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
于 2008-11-26T14:26:25.577 に答える
4

これはうまくいくようです:

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を使用して独自のパーサーをロールするか、入力された数値に対して「のみ」の2倍の精度を受け入れる必要があります。このアプリで国防総省の予算を計画しているのでない限り、後者をお勧めします。現実的には、doubleは小数点以下14桁まで正確であるため、1兆ドル未満であれば、1セント未満になります。プロジェクトのNSDateFormatterに基づいて独自の日付解析ルーチンを作成する必要があり、文字通り1か月を費やして、すべての面白いエッジケースを処理しました(スウェーデンだけが長い日付に曜日を含めるように)。

于 2008-11-25T16:23:34.577 に答える
0

C++ で通貨値を格納する最良の方法も参照してください。

通貨を処理する最善の方法は、通貨の最小単位に整数値を使用することです。つまり、ドル/ユーロにはセントなどを使用します。コード内の浮動小数点関連の精度エラーを回避できます。

そのことを念頭に置いて、通貨値を含む文字列を解析する最善の方法は、手動で (構成可能な小数点文字を使用して) 行うことです。文字列を小数点で分割し、最初と 2 番目の部分の両方を整数値として解析します。次に、それらから組み合わせた値を作成します。

于 2008-11-26T16:32:59.517 に答える