2009-04-15 20 views
8

Tại sao chương trình sau in nội dung được in?Tại sao số học dấu chấm động trong C# không chính xác?

class Program 
{ 
    static void Main(string[] args) 
    { 
     float f1 = 0.09f*100f; 
     float f2 = 0.09f*99.999999f; 

     Console.WriteLine(f1 > f2); 
    } 
} 

Output là

false 
+0

Tại sao bạn không thử dùng thử? –

+10

Tôi đã cố gắng, tôi chỉ muốn biết tại sao tôi thấy những gì tôi thấy. – Prankster

+1

[ngôn ngữ thuyết bất khả tri - Toán học dấu chấm động có bị hỏng không?] (Http://stackoverflow.com/q/588004/995714) –

Trả lời

30

Floating point chỉ có rất nhiều chữ số chính xác. Nếu bạn nhìn thấy f1 == f2, đó là bởi vì bất kỳ sự khác biệt nào đòi hỏi độ chính xác cao hơn so với float 32 bit có thể đại diện.

tôi khuyên bạn nên đọc What Every Computer Scientist Should Read About Floating Point

+0

+1 Tại sao, tại sao tôi thấy sau khi nhấn nút Gửi mà ai đó đã đăng liên kết? : P – dirkgently

+1

+1 http://msdn.microsoft.com/en-us/library/b1e65aza(VS.71).aspx Michael đúng, C# float chỉ có độ chính xác 7 chữ số và 99.999999f có 8 chữ số. – James

+6

Đây không phải là C# cụ thể. Một phao IEEE-754 chính xác đơn sẽ chỉ có 32 bit, cung cấp khoảng 7 chữ số thập phân chính xác. Nếu bạn muốn tốt hơn, sử dụng một đôi. – Wedge

5

Điều quan trọng là đây không phải chỉ là Net: đó là một hạn chế của underlying system most every language will use to represent a float trong bộ nhớ. Độ chính xác chỉ đi xa đến vậy.

Bạn cũng có thể có một số thú vị với các con số tương đối đơn giản, khi bạn xem xét rằng nó thậm chí không phải là cơ sở mười. 0,1, ví dụ, là một số thập phân lặp lại khi được biểu diễn dưới dạng nhị phân.

3

Trong trường hợp cụ thể này, đó là do .09 và .999999 không thể được biểu diễn với độ chính xác chính xác trong nhị phân (tương tự, 1/3 không thể được biểu diễn với độ chính xác trong thập phân). Ví dụ, căn cứ 0.111111111111111111101111 2 là 0.999998986721038818359375 cơ sở 10. Thêm 1 vào giá trị nhị phân trước đó, 0.11111111111111111111 cơ sở 2 là 0.99999904632568359375 cơ sở 10. Không có giá trị nhị phân cho chính xác 0,999999. Độ chính xác của điểm nổi cũng bị giới hạn bởi khoảng trống được phân bổ để lưu trữ số mũ và phần phân số của phần định trị. Ngoài ra, giống như các loại số nguyên, điểm trôi nổi có thể tràn phạm vi của nó, mặc dù phạm vi của nó lớn hơn phạm vi số nguyên.

Chạy bit này của mã C++ trong trình gỡ lỗi Xcode,

float myFloat = 0.1;

cho thấy myFloat nhận giá trị 0,100000001. Nó được tắt bởi 0.000000001. Không nhiều, nhưng nếu tính toán có một số phép tính số học, thì sự thiếu chính xác có thể được gộp lại.

IMHO một lời giải thích rất tốt của dấu chấm động là trong Chương 14 của Giới thiệu về Tổ chức máy tính với x86-64 hội Ngôn ngữ & GNU/Linux bởi Bob Plantz của Đại học bang California tại Sonoma (đã nghỉ hưu) http://bob.cs.sonoma.edu/getting_book.html. Sau đây là dựa trên chương đó.

Điểm nổi giống như ký hiệu khoa học, trong đó một giá trị được lưu trữ dưới dạng số hỗn hợp lớn hơn hoặc bằng 1,0 và nhỏ hơn 2,0 (phần định trị), nhân số khác với một số lũy thừa (số mũ). Điểm nổi sử dụng cơ số 2 thay vì cơ số 10, nhưng trong mô hình đơn giản mà Plantz đưa ra, ông sử dụng căn cứ 10 để làm rõ. Hãy tưởng tượng một hệ thống nơi hai vị trí lưu trữ được sử dụng cho phần định trị, một vị trí được sử dụng cho dấu của số mũ * (0 đại diện + và 1 đại diện -), và một vị trí được sử dụng cho số mũ. Bây giờ thêm 0,93 và 0,91. Câu trả lời là 1,8, không phải là 1,84.

9311 đại diện cho 0,93 hoặc 9,3 lần 10 đến -1.

9111 đại diện cho 0,91 hoặc 9,1 lần 10 đến -1.

Câu trả lời chính xác là 1,84 hoặc 1,84 lần 10 đến 0, sẽ là 18400 nếu chúng tôi có 5 vị trí, nhưng chỉ có bốn vị trí, câu trả lời là 1800 hoặc 1,8 lần 10 cho số không hoặc 1,8 . Tất nhiên, các kiểu dữ liệu điểm động có thể sử dụng nhiều hơn bốn vị trí lưu trữ, nhưng số lượng vị trí vẫn còn hạn chế.

Độ chính xác không giới hạn bởi khoảng trắng, nhưng "biểu diễn chính xác các giá trị phân số trong hệ nhị phân được giới hạn trong tổng các lũy thừa nghịch đảo của hai." (Plantz, op. Cit.).

0,11100110 (nhị phân) = 0,89843750 (thập phân)

0,11100111 (nhị phân) = 0,90234375 (thập phân)

Không có đại diện chính xác của 0,9 thập phân ở dạng nhị phân. Ngay cả việc mang số lượng nhỏ ra nhiều nơi cũng không hiệu quả, khi bạn lặp lại mãi mãi 1100 ở bên phải.

Bắt đầu lập trình viên thường thấy số học dấu phẩy động càng nhiều chính xác hơn số nguyên. Đúng là thậm chí thêm hai số lượng rất lớn có thể gây tràn. Phép nhân làm cho nó thậm chí nhiều khả năng là rằng kết quả sẽ rất lớn và, do đó, tràn. Và khi sử dụng với hai số nguyên, toán tử/trong C/C++ làm cho phần phân đoạn bị mất. Tuy nhiên, ... đại diện điểm nổi có bộ không chính xác của riêng mình. (Plantz, op. Cit.)

* Trong dấu phẩy động, cả ký hiệu số và ký hiệu của số mũ được biểu diễn.

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