2010-02-10 30 views
8

Sử dụng Java trên PC Windows 7 (không chắc chắn) và gọi Math.cos() trên các giá trị trả về 0 (như pi/2) trả về các giá trị nhỏ, nhưng các giá trị nhỏ, trừ khi tôi hiểu lầm, lớn hơn nhiều so với 1 ulp từ 0.Phương thức Java Math.cos() không trả về 0 khi được mong đợi

Math.cos(Math.PI/2) = 6.123233995736766E-17 
Math.ulp(Math.cos(Math.PI/2)) = 1.232595164407831E-32 

Đây có phải là thực tế trong vòng 1 ulp và tôi chỉ đơn giản là nhầm lẫn? Và đây có phải là một phương pháp bao bọc có thể chấp nhận được để giải quyết sự không chính xác nhỏ này không?

public static double cos(double a){ 
    double temp = Math.abs(a % Math.PI); 
    if(temp == Math.PI/2) 
     return 0; 
    return Math.cos(a); 
} 
+0

@ dimo414 : Đầu tiên là một bình luận về "bạn không chắc chắn nếu có vấn đề". Hành vi của hầu hết các hoạt động toán học không được xác định chính xác để hệ điều hành và CPU có thể quan trọng. Nếu bạn muốn hoạt động toán học có hành vi được xác định nghiêm ngặt (có thể dễ dàng khắc phục sự cố) bạn muốn sử dụng StrictMath, không phải Toán (tất nhiên các hoạt động StrictMath có thể sẽ chậm hơn nếu chúng không thể sử dụng hoạt động tăng tốc phần cứng có sẵn trong CPU). – SyntaxT3rr0r

+0

Math.cos() chỉ là một trình bao bọc cho StrictMath.cos(), mà chính nó là một hàm gốc. – dimo414

Trả lời

10

Đừng quên rằng Math.PI/2 là xấp xỉ. Nó sẽ không là chính xác pi/2, do đó kết quả của cos(Math.PI/2) sẽ không phải là chính xác 0. Math.cos có thể trả về một phiên bản chính xác của cosin của giá trị chính xác được trả về bằng cách tính Math.PI/2.

+0

Rất đúng, nhưng điều đó nói rằng, nó có vẻ trực quan với tôi rằng nếu Math.PI là đôi gần nhất với Pi, thì Math.PI/2 nên là đôi gần nhất với Pi/2. Và do định nghĩa cos() Pi/2 và 3Pi/2 bằng không, có vẻ như khi xấp xỉ gần gấp đôi của những con số đó được chuyển tới Math.cos(), nó cũng phải bằng không. Có lẽ điều này chỉ là phẳng ra xấu, (đó là mấu chốt của câu hỏi của tôi) nhưng điều đó làm cho cảm giác trực quan với tôi. – dimo414

7

Bạn không bao giờ nên sử dụng == với số nhân đôi. Bạn phải luôn luôn làm trong phạm vi vi lỗi. 10 -17 là độ chính xác cao nếu bạn hỏi tôi. Hình ảnh của 10p -32 chỉ là giá trị trước của gấp đôi trong 10 -17 thứ tự độ lớn, như 2.220446049250313E-16 là độ chính xác của số ở độ cao 10 .

+0

@Slartibartfsat: +1, chính xác. Bạn đánh bại tôi vào nó. – SyntaxT3rr0r

+0

Biên độ lỗi phải ít nhất hai lần ulp (PI/2), bởi vì đó là sự thiếu chính xác của PI/2. cos có đạo hàm là -1 tại thời điểm đó, do đó, sự không chính xác của PI/2 được phản ánh trong kết quả, cộng với sự không chính xác của cos. – starblue

+0

Thông thường tôi đồng ý với bạn, nhưng tôi muốn giới hạn ngoại lệ của tôi cho ít trường hợp nhất có thể, vì vậy nếu đầu vào thậm chí là/rất/tắt Math.PI/2 thì tôi sẽ để nó tính toán nguyên bản, nhưng trường hợp đặc biệt mà nó chính xác là Math.PI/2 hoặc một trong các bội số của nó, tôi muốn nó chính xác bằng không. Một số thử nghiệm giới hạn đã cho thấy nó hoạt động, ít nhất là trên máy tính của tôi. Đây có phải là lý do ổn, hay tôi vẫn nên kiểm tra một phạm vi? – dimo414

2

Đây là lỗi phổ biến khi bạn bắt đầu, liên kết này có một cuộc thảo luận rất kỹ thuật về lý do tại sao. http://docs.sun.com/source/806-3568/ncg_goldberg.html

Nhưng trong đó là hình thức đơn giản nhất, trong cùng một cách mà chúng ta không thể chính xác đại diện cho 1/3 trong hệ thống thập phân, có giá trị mà không thể được đại diện chính xác trong hệ thống nhị phân

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