2012-11-20 36 views
7

Có thể thiết lập, thậm chí gần như thế nào, mất chính xác tối đa khi xử lý hai giá trị double trong java (cộng/trừ)? Có lẽ kịch bản trường hợp xấu nhất là khi hai số không thể được biểu diễn chính xác, và sau đó một thao tác được thực hiện trên chúng, điều này dẫn đến một giá trị không thể được biểu diễn chính xác.Java - mất tối đa độ chính xác trong một phép cộng/trừ đôi

+0

Nếu mất chính xác là một vấn đề, tôi khuyên bạn nên sử dụng BigDecimal thay vì tăng gấp đôi. –

+0

Vâng, toàn bộ điểm của câu hỏi này là để xem mức độ mất mát lớn như thế nào. double chiếm 8 byte, trong khi BigDecimal khoảng 40, và tôi cần lưu trữ rất nhiều điểm dữ liệu – Bober02

+0

Khi bạn thực hiện nhiều thao tác dấu phẩy động, hiệu suất của việc sử dụng nguyên thủy thay vì các đối tượng có thể là đáng kể. – Cephalopod

Trả lời

5

Hãy xem Math.ulp(double). ulp của một double là delta đến giá trị cao nhất tiếp theo. Ví dụ, nếu bạn thêm vào số và một nhỏ hơn ulp của số khác, bạn biết rằng việc bổ sung sẽ không có hiệu lực. Nếu bạn nhân hai số nhân đôi, bạn có thể nhân số điểm của họ để có được lỗi tối đa của kết quả.

+0

Đây chính xác là những gì tôi muốn – Bober02

7

Trường hợp xấu nhất là tất cả chính xác có thể bị mất. Điều này có thể xảy ra nếu kết quả lớn hơn số hữu hạn có thể biểu diễn lớn nhất. Sau đó, nó sẽ được lưu trữ như POSITIVE_INFINITY (hoặc NEGATIVE_INFINITY).

Về bản cập nhật của bạn, điều này có thể xảy ra với việc bổ sung.

double a = Double.MAX_VALUE; 
System.out.println(a); 
double b = a + a; 
System.out.println(b); 

Kết quả:

1.7976931348623157E308 
Infinity 

Xem nó trực tuyến: ideone

Nói chung kích thước của các lỗi đại diện là tương đối so với kích thước của con số của bạn.

+0

Xem chỉnh sửa ở trên - Tôi có nghĩa là bổ sung chủ yếu ... – Bober02

0

Bạn có thể có một cái nhìn tại các độ chính xác thực tế của nguyên liệu đầu vào của bạn, ví dụ mã bên dưới kết quả đầu ra:

input: 0.01000000000000000020816681711721685132943093776702880859375 
range: [0.0099999999999999984734433411404097569175064563751220703125 - 0.010000000000000001942890293094023945741355419158935546875] 
range size: 3.4694469519536141888238489627838134765625E-18 
input: 10000000000000000 
range: [9999999999999998 - 10000000000000002] 
range size: 4 
public static void main(String[] args) { 
    printRange(0.01); 
    printRange(10000000000000000d); 
} 

private static void printRange(double d) { 
    long dBits = Double.doubleToLongBits(d); 
    double dNext = Double.longBitsToDouble(dBits + 1); 
    double dPrevious = Double.longBitsToDouble(dBits + -1); 
    System.out.println("input: " + new BigDecimal(d)); 
    System.out.println("range: [" + new BigDecimal(dPrevious) + " - " + new BigDecimal(dNext) + "]"); 
    System.out.println("range size: " + new BigDecimal(dNext - dPrevious)); 
} 

Bạn vẫn sẽ cần phải sau đó ước tính thiệt hại trên kết quả của bạn hoạt động. Và điều đó không làm việc với các trường hợp góc (khoảng Infinity, NaN, vv).

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