2013-04-18 49 views
9

Khi chia kết quả trong một số lặp vô hạn, con số rõ ràng bị cắt ngắn để vừa với kích thước của số thập phân. Vì vậy, một cái gì đó như 1/3 trở thành một cái gì đó như 0.3333333333333333333. Nếu sau đó chúng ta nhân số đó với 3, chúng ta sẽ nhận được một cái gì đó như 0.999999999999999999 thay vì 1 như chúng ta sẽ nhận được nếu giá trị thực sự của phân số đã được bảo tồn.Làm thế nào để đối phó với các số lặp vô hạn như số thập phân?

Đây là một mẫu mã này từ bài viết MSDN về số thập phân:

decimal dividend = Decimal.One; 
decimal divisor = 3; 
// The following displays 0.9999999999999999999999999999 to the console 
Console.WriteLine(dividend/divisor * divisor); 

Điều này gây ra một vấn đề khi giá trị 0,9999999999999999999 được so sánh với 1 đối với bình đẳng. Nếu không có sự mất chính xác, chúng sẽ bằng nhau, nhưng tất nhiên trong trường hợp này sự so sánh sẽ dẫn đến sai.

Mọi người thường giải quyết vấn đề này như thế nào? Có một giải pháp thanh lịch hơn khác ngoài việc xác định một số sai sót của lỗi đối với mọi so sánh?

+0

@nathanhayfield Có vẻ như bạn đã bỏ lỡ câu cuối cùng của câu hỏi. –

+0

Tuy nhiên, đó là * cách tổng quát để thực hiện nó, hoặc tại chỗ, hoặc với một hàm có lề bên trong nó hoặc cho phép bạn chỉ định một (điều đó sẽ thanh lịch hơn về khả năng đọc, nhưng cùng một khái niệm).(Đừng quên nếu bạn đang bao giờ hiển thị kết quả cho người dùng, cũng tròn nổi để một cái gì đó có thể đọc được. Không ai muốn thấy rằng họ nợ $ 1.99999999999. Có, tôi đã nhìn thấy điều này. Nhiều lần.) – neminem

+2

Đó là một cũ và được biết đến vấn đề trong số tính toán –

Trả lời

5

Đây là vấn đề về tính toán số rất cũ và được biết đến rộng rãi. Bạn đã nói rằng bạn đang tìm kiếm một giải pháp khác ngoài việc xác định một số sai sót của lỗi đối với mọi so sánh. Một cách tiếp cận đến với tâm trí của tôi là xây dựng cây biểu thức toán học trong bộ nhớ trước và thực hiện phép tính cuối cùng. Có điều này trong tầm tay chúng ta có thể làm một số đơn giản bằng cách sử dụng một số quy tắc đã biết trước khi thực hiện các phép tính. Các quy tắc như:

  • Remove số bình đẳng trong tử số và mẫu số của một phân số nếu họ không zero
  • căn bậc hai của một bậc hai của một số là số tự
  • ...

Do đó thay vì lưu trữ 1/3 theo giá trị thập phân/kép bằng 0.33333 ... chúng tôi có thể lưu trữ một phiên bản Fraction(1, 3). Sau đó, chúng ta có thể định nghĩa tất cả các biểu thức khác như thế này để xây dựng một biểu thức hơn là thực hiện các phép tính. Cuối cùng, trước tiên chúng ta có thể đơn giản hóa biểu thức với các quy tắc ở trên và sau đó tính kết quả.

Tôi đã tìm kiếm trên web một thời gian ngắn để tìm các thư viện để thực hiện việc đó và chưa tìm thấy bất kỳ thư viện nào. Nhưng tôi chắc chắn một số có thể được tìm thấy cho các ngôn ngữ khác/nền tảng hoặc thậm chí NET.

Xin lưu ý rằng cách tiếp cận ở trên cuối cùng chỉ mang lại kết quả tốt hơn và chưa giải quyết được vấn đề này vốn có trong bản chất của tính toán số.

+0

Gói phần mềm như Waterloo Maple hoặc Mathematica làm việc này như một vấn đề khóa học. Tôi không biết của bất kỳ thư viện NET mặc dù. –

+0

Không có các thư viện .NET tôi có thể tìm thấy, tôi đã kết thúc việc tạo ra các chức năng cơ bản của riêng mình. – GBleaney

1

Chào mừng bạn đến với nỗi đau của số học dấu chấm động.

Điều bạn thực sự muốn, tất nhiên, là một lớp số và thư viện hợp lý. Dưới đây là một sự khởi đầu của một trong C#, mặc dù tôi không biết làm thế nào hoàn thành nó là (có lẽ không phải là rất):

http://www.codeproject.com/Articles/88980/Rational-Numbers-NET-4-0-Version-Rational-Computin

Có một số trong C/C++, nhưng một lần nữa, không biết làm thế nào hữu ích/hoàn thành chúng.

Đối với dấu phẩy động:

Hãy xem một số tài nguyên. Đầu tiên, kinh điển của David Goldberg, "Mỗi nhà khoa học máy tính nên biết gì về số học dấu chấm động". Đây là tóm tắt:

Số học dấu chấm động được coi là chủ đề bí truyền của nhiều người. Điều này khá đáng ngạc nhiên, bởi vì dấu phẩy động là phổ biến trong các hệ thống máy tính: Hầu như mọi ngôn ngữ đều có kiểu dữ liệu dấu phẩy động; máy tính từ máy tính đến siêu máy tính có máy gia tốc điểm nổi; hầu hết các trình biên dịch sẽ được gọi để biên dịch các thuật toán dấu phẩy động từ theo thời gian; và hầu như mọi hệ điều hành đều phải trả lời các ngoại lệ dấu phẩy động chẳng hạn như tràn. Bài báo này trình bày một hướng dẫn về các khía cạnh của dấu phẩy động có tác động trực tiếp đối với các nhà thiết kế hệ thống máy tính. Nó bắt đầu với nền tảng trên dấu phẩy động và lỗi làm tròn , tiếp tục với một cuộc thảo luận về tiêu chuẩn điểm nổi IEEE và kết thúc bằng các ví dụ về cách các nhà xây dựng hệ thống máy tính có thể điểm nổi hỗ trợ tốt hơn.

Bạn có thể tải về một phiên bản chỉnh sửa của giấy, miễn phí từ nhiều nơi:

Ngoài ra, bản gốc phải sẵn có tại tòa nhà gạch lớn với tất cả các sách. Bản trích dẫn đầy đủ là

David Goldberg. 1991. Điều mà mọi nhà khoa học máy tính nên biết về số điểm động . ACM Comput. Surv. 23, 1 (tháng 3 năm 1991), 5-48. DOI = 10,1145/103162,103163 http://doi.acm.org/10.1145/103162.103163

Sau đó, hãy nhìn vào những tài nguyên:

+0

Microsoft Solver Foundation vận chuyển bằng một lớp Rational. –

+0

@EricLippert: Tôi không biết điều đó. Cảm ơn! –

0

Nếu bạn đi với cách tôi commented lên rồi lưu trữ nó vào cơ sở dữ liệu cũng sẽ không phải là vấn đề. Bạn có thể lưu trữ nó với kiểu Phân số trong db. Xem UDT.

2

Như bạn đã đề cập, kết quả tính toán với việc sử dụng các số dấu phẩy động phải được 'trang bị' thành biểu diễn dấu phẩy động. Đó là lý do tại sao so sánh chính xác không phải là một ý tưởng tốt - một số dung sai là bắt buộc. Vì vậy, thay vì x == y, Math.Abs(x - y) < tolerance nên được sử dụng.

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