2008-11-25 34 views
15

Tôi có một số chuỗi là ngôn ngữ cụ thể (ví dụ: 0,01 hoặc 0,01). Tôi muốn chuyển đổi chuỗi này thành một NSDecimalNumber. Từ examples I've seen thus far on the interwebs, điều này được thực hiện bằng cách sử dụng một NSNumberFormatter a la:Lấy NSDecimalNumber từ một chuỗi địa phương cụ thể?

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 

Tôi đang sử dụng 10,4 chế độ (ngoài việc bị đề nghị mỗi tài liệu, nó cũng là chế độ chỉ có sẵn trên iPhone) nhưng chỉ với trình định dạng mà tôi muốn tạo số thập phân. Lưu ý rằng tôi đã đơn giản hóa ví dụ của mình (tôi thực sự đang xử lý các chuỗi tiền tệ). Tuy nhiên, tôi rõ ràng đang làm sai điều gì đó để trả về một giá trị minh họa sự thiếu chính xác của các số dấu phẩy động.

Phương pháp chính xác để chuyển đổi chuỗi số địa phương cụ thể thành số NSDecimalNumber là gì?

Chỉnh sửa: Lưu ý rằng ví dụ của tôi là để đơn giản. Câu hỏi tôi hỏi cũng nên liên quan đến khi bạn cần phải lấy một chuỗi tiền tệ địa phương cụ thể và chuyển đổi nó thành một NSDecimalNumber. Ngoài ra, điều này có thể được mở rộng đến một chuỗi tỷ lệ phần trăm cụ thể của miền địa phương và chuyển đổi nó thành một NSDecimalNumber.

Trả lời

13

Dựa trên câu trả lời của Boaz Stuller, tôi đã đăng lỗi cho Apple về vấn đề này. Cho đến khi điều đó được giải quyết, đây là những cách giải quyết mà tôi đã quyết định là cách tiếp cận tốt nhất để thực hiện. Những cách giải quyết này chỉ đơn giản dựa vào làm tròn số thập phân đến độ chính xác phù hợp, đó là một cách tiếp cận đơn giản có thể bổ sung mã hiện có của bạn (thay vì chuyển đổi từ trình định dạng sang máy quét).

số chung

Về cơ bản, tôi chỉ làm tròn số dựa trên các quy tắc mà làm cho ý nghĩa đối với hoàn cảnh của tôi. Vì vậy, YMMV tùy thuộc vào độ chính xác mà bạn hỗ trợ.

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 

Đơn vị tiền tệ

Xử lý tiền tệ (đó là vấn đề thực tế tôi đang cố gắng để giải quyết) chỉ là sự thay đổi nhỏ về xử lý số nói chung. Điều quan trọng là quy mô của hành vi làm tròn được xác định bằng các chữ số thập phân tối đa được sử dụng bởi đơn vị tiền tệ của miền địa phương.

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

Chỉ cần một lưu ý phụ, bạn nên bỏ số của trình định dạng tới con trỏ tới NSDecimalNumber. I E. NSDecimalNumber * decimalNumber = (NSDecimalNumber *) [mã định dạng numberFromString: s]; –

3

Điều này dường như làm việc:

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 

Ngoài ra, báo lỗi về vấn đề này. Đó chắc chắn không phải là hành vi chính xác mà bạn đang thấy ở đó.

Chỉnh sửa: Cho đến khi Apple sửa lỗi này (và sau đó mọi người dùng cập nhật phiên bản OSX cố định), có thể bạn sẽ phải cuộn trình phân tích cú pháp của riêng mình bằng NSScanner hoặc chấp nhận độ chính xác kép 'duy nhất' cho số đã nhập. Trừ khi bạn đang có kế hoạch để có ngân sách Lầu Năm Góc trong ứng dụng này, tôi muốn đề nghị sau này. Thực tế, gấp đôi là chính xác đến 14 chữ số thập phân, vì vậy ở bất cứ điều gì ít hơn một nghìn tỷ đô la, chúng sẽ ít hơn một xu. Tôi đã phải viết thói quen phân tích cú pháp ngày của riêng tôi dựa trên NSDateFormatter cho một dự án và tôi đã dành một tháng để xử lý tất cả các trường hợp cạnh tranh vui nhộn, (giống như cách mà Thụy Điển có ngày trong tuần có trong ngày dài).

+0

Lỗi # 6400434 đã đăng nhập. Cảm ơn bạn đã trả lời. Như tôi đã đề cập, ví dụ của tôi đã được đơn giản hóa (tôi thực sự đang xử lý tiền tệ). Vì vậy, tôi không chắc chắn câu trả lời này sẽ làm việc cho kịch bản này. – shek

+0

Boaz Stuller - xem câu trả lời của tôi minh họa một đoạn mã xử lý vấn đề này khá đơn giản, mà không cần sử dụng trình phân tích cú pháp tùy chỉnh/NSScanner ... và dưới ngân sách Lầu Năm Góc. ;) – shek

0

cũng Xem Best way to store currency values in C++

Cách tốt nhất để xử lý tiền tệ là sử dụng một giá trị số nguyên cho đơn vị nhỏ nhất của tiền tệ, ví dụ cent đối với đồng USD/euro, vvBạn sẽ tránh được bất kỳ lỗi chính xác liên quan đến dấu phẩy động nào trong mã của bạn.

Với ý nghĩ đó, cách tốt nhất để phân tích chuỗi chứa giá trị tiền tệ là thực hiện thủ công (với ký tự dấu thập phân có thể định cấu hình). Tách chuỗi tại dấu thập phân và phân tích cú pháp cả phần đầu tiên và phần thứ hai dưới dạng giá trị số nguyên. Sau đó, sử dụng xây dựng giá trị kết hợp của bạn từ những giá trị đó.

+0

Cảm ơn câu trả lời của bạn, chưa sử dụng. Ý kiến ​​của bạn là một quy tắc tốt để xử lý tiền tệ (mà tôi đã làm). Tuy nhiên, thay vì phân tích cú pháp chuỗi tiền tệ đầu vào theo cách thủ công như bạn đã mô tả, hãy xem các ví dụ mã của tôi trong câu trả lời xử lý vấn đề này một cách thanh lịch hơn. – shek

+0

Cụ thể, ví dụ mã của tôi thể hiện việc xử lý các đầu vào Chuỗi tiền tệ trên mọi miền địa phương. Điều này dễ dàng hơn nhiều so với việc phải phân tích cú pháp chuỗi và tài khoản cho các quy tắc cụ thể khác nhau về nhóm, phân tách thập phân, ký hiệu tiền tệ, v.v. – shek

18

Năm sau:

+(NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString trong NSDecimalNumber.

+2

Cẩn thận với dấu tách thập phân khác nhau ở một số vùng. Ví dụ: bằng tiếng Pháp, chúng tôi sử dụng dấu phẩy "," để tách các số thập phân. Dòng này sẽ không chuyển đổi đúng trong trường hợp đó. '[NSDecimalNumber decimalNumberWithString: string locale: [NSLocale currentLocale]]' hoạt động tốt, tuy nhiên :) – aspyct

Các vấn đề liên quan