2010-07-12 19 views
11

Làm thế nào để bạn đối phó với hành vi kỳ lạ của Java với toán tử mô-đun khi sử dụng đôi?Mô-đun với đôi trong Java

Ví dụ, bạn mong chờ kết quả của 3.9 - (3.9 % 0.1)3.9 (và quả thật, Google nói tôi sẽ không điên), nhưng khi tôi chạy nó trong Java tôi nhận được 3.8000000000000003.

Tôi hiểu đây là kết quả của cách cửa hàng và quy trình Java tăng gấp đôi, nhưng có cách nào để giải quyết nó không?

+1

bạn đang cố gắng làm gì đây? Tôi có cảm giác rằng bạn thực sự không cần '%' chút nào. – polygenelubricants

Trả lời

15

Sử dụng một loại chính xác nếu bạn cần một kết quả chính xác:

double val = 3.9 - (3.9 % 0.1); 
    System.out.println(val); // 3.8000000000000003 

    BigDecimal x = new BigDecimal("3.9"); 
    BigDecimal bdVal = x.subtract(x.remainder(new BigDecimal("0.1"))); 
    System.out.println(bdVal); // 3.9 

Tại sao 3,8000 ... 003? Bởi vì Java sử dụng FPU để tính toán kết quả. 3.9 là không thể lưu trữ chính xác trong ký hiệu chính xác kép IEEE, do đó, nó lưu trữ 3.89999 ... thay thế. Và 3,8999% 0,01 cho 0,09999 ... do đó kết quả là lớn hơn một chút so với 3,8.

+0

+1 Tôi ghét làm việc với các số điểm nổi IEEE bằng tay trong lớp Kỹ thuật điện của tôi, nhưng nó đáng giá. –

+0

Cảm ơn, điều đó sẽ hoạt động tốt. – Andy

+0

Câu trả lời hay. Vậy tại sao FPU có thể tìm ra rằng '((3.9D/0.1D)% 1D) = 0.0' (về cơ bản cùng một vấn đề hơi tái tổ chức) khi do lỗi vòng nó nghĩ '(3.9D% 0.1D) = 0.0999999999999997'? –

1

Nếu bạn biết số lượng thập phân bạn đang xử lý, trước tiên bạn có thể thử chuyển đổi thành số nguyên. Đây chỉ là một trường hợp cổ điển của inacuraccy điểm nổi. Thay vì làm 3.9 % 0.1, bạn đang làm một cái gì đó như 3.899 % 0.0999

+1

Và Google sẽ tuyên bố rằng 3,9% 0,1 = -4,4408921 × 10-16 mà không hoàn toàn phù hợp với mô đun hoặc. – Stroboskop

2

Từ The Java Language Specification:

Kết quả của một hoạt động còn lại dấu chấm động như tính bởi các nhà điều hành % là không giống như sản xuất bởi các hoạt động còn lại định nghĩa bởi IEEE 754. Các IEEE 754 hoạt động còn lại tính phần còn lại từ một bộ phận làm tròn, không phải là một bộ phận cắt ngắn, và do đó hành vi của nó không tương tự như của toán tử số dư còn lại thông thường. Thay vào đó, ngôn ngữ lập trình Java xác định% trên các hoạt động điểm động để hoạt động theo cách tương tự với của số dư còn lại toán tử; điều này có thể được so sánh với chức năng thư viện C fmod. Hoạt động còn lại của IEEE 754 có thể là được tính theo thói quen thư viện Math.IEEEremainder.

Nói cách khác, điều này là do thực tế là Java làm tròn kết quả của bộ phận liên quan đến tính toán phần còn lại, trong khi IEEE754 chỉ định cắt ngắn câu trả lời của bộ phận. Trường hợp đặc biệt này dường như phơi bày sự khác biệt này rất rõ ràng.

Bạn có thể nhận được câu trả lời bạn mong đợi sử dụng Math.IEEEremainder:

System.out.println(3.9 - (3.9 % 0.1)); 
System.out.println(3.9 - Math.IEEEremainder(3.9, 0.1)); 
Các vấn đề liên quan