2013-03-25 36 views
22

Vì vậy, khi tôi cộng hoặc trừ trong Java với Doubles, nó cho tôi kết quả lạ. Đây là một số:Cộng và trừ đôi được cho kết quả lạ

Nếu tôi thêm 0.0 + 5.1, nó cho tôi 5.1. Đúng rồi.

Nếu tôi thêm 5.1 + 0.1, nó cung cấp cho tôi 5.199999999999 (Số lượng lặp lại 9 có thể bị tắt). Sai rồi.

Nếu tôi trừ 4.8 - 0.4, nó cho tôi 4.39999999999995 (Xin nhắc lại, số 9 có thể bị tắt). Sai rồi.

Lúc đầu, tôi nghĩ đây chỉ là vấn đề với việc tăng gấp đôi giá trị thập phân, nhưng tôi đã sai. Những điều sau đây đã làm việc tốt:

5.1 + 0.2 = 5.3 
5.1 - 0.3 = 4.8 

Bây giờ, số đầu tiên được thêm là một lần lưu đôi là biến, mặc dù biến thứ hai lấy văn bản từ JTextField. Ví dụ:

//doubleNum = 5.1 RIGHT HERE 
//The textfield has only a "0.1" in it. 
doubleNum += Double.parseDouble(textField.getText()); 
//doubleNum = 5.199999999999999 
+0

btw, thích sử dụng BigDecimal (http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html) – user2147970

+0

Thực ra, 5.19999999 ... (với nhiều lần lặp lại 9 lần) là toán học chính xác giống như 5.2, nếu bạn không tin, hãy cố gắng tìm ra sự khác biệt giữa hai yếu tố này. Nó là 0, do đó, chúng giống nhau. – Ingo

+0

@ Về mặt kỹ thuật, vâng, nhưng tôi muốn có một câu trả lời chính xác một chút :) –

Trả lời

24

Trong Java, double giá trị là IEEE floating point numbers. Trừ khi chúng là sức mạnh của 2 (hoặc tổng lũy ​​thừa của 2, ví dụ 1/8 + 1/4 = 3/8), chúng không thể được biểu diễn chính xác, ngay cả khi chúng có độ chính xác cao. Một số thao tác dấu chấm động sẽ kết hợp lỗi vòng tròn xuất hiện trong các số dấu phẩy động này. Trong trường hợp bạn đã mô tả ở trên, các lỗi dấu phẩy động đã trở nên đủ quan trọng để hiển thị trong đầu ra.

Không quan trọng nguồn của số là gì, cho dù đó là phân tích cú pháp một chuỗi từ một số JTextField hoặc chỉ định một chữ cái double - vấn đề được kế thừa trong biểu diễn dấu phẩy động.

Cách giải quyết:

  • Nếu bạn biết bạn sẽ chỉ có rất nhiều điểm thập phân, sau đó sử dụng số nguyên số học, sau đó chuyển sang một số thập phân:

    (double) (51 + 1)/10 
    (double) (48 - 4)/10 
    
  • Sử dụng BigDecimal

  • Nếu bạn phải sử dụng double, bạn có thể cắt giảm các lỗi dấu phẩy động với số Kahan Summation Algorithm.

+0

Bạn có nghĩ rằng nếu tôi sử dụng 'float' thay vào đó nó sẽ cung cấp cho một giá trị chính xác hơn? –

+0

Một 'float' thường chỉ có một nửa độ chính xác của' double'. Đó không phải là một ý tưởng tốt nếu bạn cần độ chính xác. – rgettman

+0

@Ingo: Bắt tốt; Tôi đã cập nhật câu trả lời của mình. – rgettman

2

Trong Java, nhân đôi sử dụng số học dấu chấm động IEEE 754 (xem this Bài viết trên Wikipedia), vốn không chính xác. Sử dụng BigDecimal để có độ chính xác thập phân hoàn hảo. Để làm tròn in, chỉ chấp nhận độ chính xác "khá tốt", hãy sử dụng printf("%.3f", x).

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