Các phao chính xác vấn đề
Bạn có hai vấn đề ở đây, nhưng cả hai đều xuất phát từ cùng một gốc
Bạn không thể so sánh nổi một cách chính xác. Bạn không thể trừ hoặc chia chúng một cách chính xác. Bạn không thể đếm được mọi thứ cho chính xác. Bất kỳ hoạt động với họ có thể (và hầu như luôn luôn) mang lại một số lỗi vào kết quả. Thậm chí a=0.2f
không phải là một hoạt động chính xác. Những lý do sâu sắc hơn được giải thích rất rõ bởi các tác giả của các câu trả lời khác ở đây. (Cảm ơn của tôi và phiếu bầu cho họ cho điều đó.)
Ở đây có lỗi đầu tiên và đơn giản hơn của bạn. Bạn sẽ không bao giờ, không bao giờ , bao giờ, bao giờ, KHÔNG BAO GIỜ sử dụng trên chúng == hoặc tương đương trong ngôn ngữ bất kỳ.
Thay vì a==b
, hãy sử dụng Abs(a-b)<HighestPossibleError
để thay thế.
Nhưng đây không phải là vấn đề duy nhất trong nhiệm vụ của mình.
Abs(1/y-x)<HighestPossibleError
cũng không hoạt động. Ít nhất, nó sẽ không hoạt động thường xuyên. Tại sao?
Hãy lấy cặp x = 1000 và y = 0,001. Hãy lấy lỗi "bắt đầu" tương đối của y cho 10 -6.
(Lỗi tương đối = lỗi/giá trị).
Lỗi tương đối của các giá trị được thêm vào lúc nhân và chia.
1/y là khoảng 1000. Lỗi tương đối của nó giống nhau 10 -6. ("1" không có lỗi)
Điều đó làm cho lỗi tuyệt đối = 1000 * 10 -6 = 0,001. Khi bạn trừ x sau, lỗi đó sẽ là tất cả những gì còn lại. (Lỗi tuyệt đối được thêm vào lúc cộng và trừ, và lỗi của x là không đáng kể nhỏ.) Chắc chắn, bạn không tính đến những lỗi quá lớn, HighestPossibleError chắc chắn sẽ được đặt thấp hơn và chương trình của bạn sẽ ném ra một cặp x, y
Vì vậy, hai quy tắc tiếp theo cho hoạt động nổi: cố gắng không chia valuer bởi ít hơn một và Thiên Chúa giúp bạn tiết kiệm từ trừ các giá trị gần sau đó.
Có hai cách đơn giản để thoát khỏi vấn đề này.
Bằng sáng lập những gì của x, y có giá trị lớn hơn abs và chia 1 bởi một càng lớn và chỉ sau đó trừ một trong những ít.
Nếu bạn muốn so sánh 1/y against x
, trong khi bạn đang làm việc nhưng với chữ cái, không giá trị, và hoạt động của bạn làm cho không có lỗi, nhân bên cả hai so sánh bằng y và bạn có 1 against x*y
. (Thông thường bạn nên kiểm tra các dấu hiệu trong hoạt động đó, nhưng ở đây chúng tôi sử dụng giá trị abs, vì vậy, nó là sạch.) Kết quả so sánh không có sự phân chia nào cả.
Trong một cách ngắn:
1/y V x <=> y*(1/y) V x*y <=> 1 V x*y
Chúng tôi đã biết rằng so sánh như 1 against x*y
nên được thực hiện như vậy:
const float HighestPossibleError=1e-10;
if(Abs(x*y-1.0)<HighestPossibleError){...
Đó là tất cả.
P.S. Nếu bạn thực sự cần nó tất cả trên cùng một dòng, sử dụng:
if(Abs(x*y-1.0)<1e-10){...
Nhưng đó là phong cách xấu. Tôi sẽ không tư vấn cho nó.
P.P.S. Trong ví dụ thứ hai của bạn trình biên dịch tối ưu hóa mã như vậy, rằng nó đặt z đến 5 trước khi chạy bất kỳ mã nào. Vì vậy, kiểm tra 5 chống lại 5 hoạt động ngay cả đối với phao nổi.
http://docs.oracle.com/ cd/E19957-01/806-3568/ncg_goldberg.html – Mysticial
'((x * y) == 1)' không hoạt động? – Vyktor
Tôi đã thêm một số thông tin trong câu trả lời của mình. Và +1 cho một câu hỏi hiệu quả. – Gangnus