2009-09-08 32 views
5

tôi đã được thử nghiệm mã này từ Brainteasers:Không phải là độ chính xác của Double Type là 15 chữ số trong C#?

 double d1 = 1.000001; 

     double d2 = 0.000001; 

     Console.WriteLine((d1 - d2) == 1.0); 

Và kết quả là "False". Khi tôi thay đổi loại dữ liệu:

 decimal d1 = 1.000001M; 

     decimal d2 = 0.000001M; 

     decimal d3 = d1-d2; 

     Console.WriteLine(d3 == 1); 

Chương trình viết câu trả lời đúng: "True".

Sự cố này chỉ sử dụng 6 chữ số sau dấu phẩy động. Điều gì đã xảy ra với độ chính xác 15 chữ số?

Trả lời

28

Điều này không liên quan gì đến độ chính xác - nó phải làm với lỗi làm tròn đại diện.

System.Decimal có khả năng biểu thị số dấu phẩy động lớn với giảm đáng kể nguy cơ phát sinh bất kỳ lỗi làm tròn nào giống như lỗi bạn đang thấy. System.SingleSystem.Double không có khả năng này và sẽ làm tròn những con số này ra và tạo ra các vấn đề như một trong những bạn đang thấy trong ví dụ của bạn.

System.Decimal sử dụng hệ số chia tỷ lệ để giữ vị trí thập phân, cho phép biểu diễn chính xác số lượng dấu phẩy đã cho, trong khi System.SingleSystem.Double chỉ gần đúng giá trị của bạn nhất có thể.

Để biết thêm thông tin, vui lòng xem System.Double:

Hãy nhớ rằng một số dấu chấm động chỉ có thể xấp xỉ một số thập phân, và độ chính xác của một số dấu chấm động xác định cách chính xác mà số xấp xỉ một số thập phân . Theo mặc định, giá trị Double chứa 15 chữ số thập phân của độ chính xác , mặc dù tối đa 17 chữ số được duy trì trong nội bộ. Các độ chính xác của một số dấu chấm động có một số hậu quả:

  • Hai số dấu chấm động xuất hiện bình đẳng cho một độ chính xác đặc biệt có thể không so sánh bằng vì ít nhất họ đáng kể chữ số là khác nhau.

  • Một phép toán hoặc so sánh có sử dụng một dấu chấm động số có thể không mang lại hiệu quả tương tự nếu một số thập phân được sử dụng vì số dấu chấm động có thể không chính xác xấp xỉ số thập phân số.

+0

http://msdn.microsoft.com/en-us/library/system.decimal.aspx "Loại thập phân không loại bỏ nhu cầu làm tròn. Thay vào đó, nó giảm thiểu lỗi do làm tròn. Ví dụ: mã tạo ra kết quả là 0,9999999999999999999999999999 thay vì 1 " –

+0

@quant_dev: Đủ công bằng, tôi đã chỉnh sửa câu trả lời của mình để phản ánh điều đó :) –

+0

"System.Decimal sử dụng hệ số chia tỷ lệ để giữ vị trí của chữ số thập phân, cho phép biểu diễn chính xác một số dấu phẩy động" - không đúng; ví dụ, 1/3 sẽ không được biểu diễn chính xác không phải bằng cách tăng gấp đôi (sử dụng cơ sở nhị phân) hay số thập phân (sử dụng số thập phân). Trong thực tế, sự khác biệt duy nhất giữa Double và Decimal là cơ sở, số bit được dành riêng cho số mũ và số mũ, và liệu số mũ có thể thay đổi dấu (có cho Double, no for Decimal) hay không. Ngoài ra, chúng giống nhau. System.Decimal không có độ chính xác tùy ý! –

3

Ý tưởng về điểm nổi số là họ không phải là chính xác đối với một số cụ thể của chữ số. Nếu bạn muốn loại chức năng đó, bạn nên xem loại dữ liệu decimal.

2

Tránh so sánh về bình đẳng đối với các số dấu phẩy động.

3

Độ chính xác không tuyệt đối vì không thể chuyển đổi chính xác giữa số thập phân và số nhị phân.

Trong trường hợp này, .1 thập phân lặp lại mãi mãi khi được biểu diễn bằng nhị phân. Nó chuyển đổi thành .000110011001100110011 ... và lặp lại mãi mãi. Không có số lượng chính xác nào sẽ lưu trữ chính xác.

8

Nói chung, cách để kiểm tra cho sự bình đẳng của các giá trị dấu chấm động là để kiểm tra gần -equality, ví dụ: kiểm tra sự khác biệt đó là gần với giá trị nhỏ nhất (gọi là epsilon) cho datatype đó. Ví dụ,

if (Math.Abs(d1 - d2) <= Double.Epsilon) ... 

này kiểm tra để xem nếu d1d2 được đại diện bởi các mẫu bit cùng cho hoặc mất các bit quan trọng nhất.

Correction(thêm 2 Mar 2015)

Sau khi kiểm tra kỹ hơn, mã nên được nhiều như thế này:

// Assumes that d1 and d2 are not both zero 
if (Math.Abs(d1 - d2)/Math.Max(Math.Abs(d1), Math.Abs(d2)) <= Double.Epsilon) ... 

Nói cách khác, lấy chênh lệch tuyệt đối giữa d1d2 , sau đó mở rộng tỷ lệ bằng số lớn nhất là d1d2rồi so sánh nó với Epsilon.

Tài liệu tham khảo
http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx
http://msdn.microsoft.com/en-us/library/system.double.aspx#Precision

+0

Trong liên kết của bạn, bạn có thể đọc Epsilon là số dương nhỏ nhất Double và không tương đương với epsilon máy, đại diện cho giới hạn trên của thân nhân lỗi do làm tròn trong số học dấu phẩy động. Vậy tại sao câu trả lời của bạn mâu thuẫn với liên kết bạn đăng? – Nicolas

4

Các thập phân loại dụng cụ thập phân dấu chấm động trong khi đôi là dấu chấm động nhị phân.

Lợi thế của số thập phân là nó hoạt động như một con người đối với làm tròn, và nếu bạn khởi tạo nó với giá trị thập phân, thì giá trị đó được lưu chính xác như bạn đã chỉ định. Điều này chỉ đúng với số thập phân có chiều dài hữu hạn và nằm trong phạm vi và độ chính xác thể hiện được. Nếu bạn khởi tạo nó với nói 1.0M/3.0M, sau đó nó sẽ không được lưu trữ chính xác giống như bạn sẽ viết 0.33333-định kỳ trên giấy.

Nếu bạn khởi tạo giá trị FP nhị phân bằng số thập phân, nó sẽ được chuyển đổi từ dạng thập phân có thể đọc được của con người thành biểu diễn nhị phân hiếm khi có cùng giá trị.

Mục đích chính của loại số thập phân là để triển khai các ứng dụng tài chính. hoạt động.

Lưu ý rằng gấp đôi chính xác đến khoảng 15 chữ số có nghĩa không 15 chữ số thập phân.d1 được khởi tạo với 7 giá trị chữ số có nghĩa là không 6, trong khi d2 chỉ có 1 chữ số có nghĩa. Thực tế là chúng có độ lớn khác nhau đáng kể cũng không giúp được gì.

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