2012-07-16 29 views
5

thể trùng lặp:
Retain precision with Doubles in javaJava chính xác đôi với -hằng/bộ phận

Bạn có biết sự khác biệt giữa hai hoạt động này trong Java.

final double m1 = 11d/1e9; // gives 1.1e-8 
final double m2 = 11d * 1e-9; // gives 1.1000000000000001e-8 

Tôi thấy trong bytecode được tạo ra mà kết quả biên dịch trước của m2 không phải là điều tôi mong đợi.
Trong đầu ra của javap -verbose -c tôi có thể nhìn thấy giá trị sau:

const #3 = double 1.1E-8d; 
[...] 
const #6 = double 1.1000000000000001E-8d; 

Khi tôi sử dụng m1 hoặc m2 trong các biểu thức khác, tôi không có kết quả tương tự.

Khi tôi cố gắng điều tương tự trong C, m1 và m2 là đúng 1.1e-8

Tôi nghĩ rằng vấn đề của tôi là trong cách xử lý java đôi tính toán chính xác nhưng tôi không thể giải thích bản thân mình những gì tôi bỏ lỡ.

Trả lời

1

Các sự khác biệt mà bạn có là 1e9 chính xác là có thể biểu diễn được và 1e-9 không chính xác có thể biểu diễn được. Lỗi đại diện nhỏ dẫn đến lỗi số học mà bạn có thể thấy.

System.out.println(new BigDecimal(1e9)); 
System.out.println(new BigDecimal(1e-9)); 

in

1000000000 
1.0000000000000000622815914577798564188970686927859787829220294952392578125E-9 

Sự khác biệt không phải là cho dù bạn đã sử dụng * hoặc / nhưng mà bạn đã sử dụng một số nguyên trong một trường hợp và một phần nhỏ trong khác.

System.out.println(11 * 1e9); 
System.out.println(11 * 1e-9); 

in

1.1E10 
1.1000000000000001E-8 
+0

Ok đó là giải thích cho Java nhưng bây giờ tại sao khi tôi thực thi cùng một mã trong C tôi có tất cả độ chính xác mà tôi cần? – Joker

+0

Trình biên dịch C sẽ tối ưu hóa mã * nhiều * xa hơn 'javac' sẽ. Điều này có nghĩa là nó có thể dịch '/ 1e-9' thành' * 1e9' vì a) nhanh hơn và b) chính xác hơn.Trình biên dịch của Java thực hiện rất ít tối ưu hóa, điều này còn lại với JIT, điều này cũng có nghĩa là mã tối ưu hóa và tối ưu hóa phải làm điều tương tự, nếu không bạn sẽ thấy thay đổi về hành vi trong khi chương trình đang chạy. Việc chuyển đổi một 'double' thành một String cũng khác nhau trong C và Java, nhưng nó ít có khả năng là nguyên nhân của sự khác biệt. Trong cả hai trường hợp, họ sử dụng cùng một FPU. –

+1

Ngoài ra trình biên dịch C cũng có thể sử dụng các thanh ghi/tính toán điểm nổi 80bit mà máy tính của bạn có. Đây là nền tảng phụ thuộc và chỉ có sẵn trên bộ xử lý x86/x64. Java sẽ hoạt động giống nhau trên mọi nền tảng vì vậy sẽ chỉ sử dụng điểm nổi 64 bit ngay cả khi điểm nổi 80 bit có sẵn. –

3

Không có sự khác biệt giữa Java và C, cả hai đều tuân thủ tiêu chuẩn điểm nổi IEEE. Sự khác biệt duy nhất có thể là cách bạn in nó. Số chính xác 1.1e-8 không thể đại diện là double. Sự khác biệt trong đầu ra có thể được quy cho các chi tiết về cách lỗi của đại diện được tích lũy, và làm tròn được sử dụng để in kết quả.

+0

có nhưng nó chia 1 đến 10^9 – Pooya

+0

Tôi nghĩ rằng sự khác biệt là chỉ trong có bao nhiêu lỗi đã được tích lũy, nơi dòng thứ hai chỉ bước qua ngưỡng cửa đó được in ra. –

0

Nếu bạn sử dụng BigDecimal như sau:

BigDecimal a=new BigDecimal(1e-9); 
BigDecimal b=new BigDecimal(11); 
System.out.println(a.multiply(b).toString()); 

bạn sẽ thấy rằng số của bạn là

1.10000000000000006850975060355784206078677556206457666121423244476318359375E-8 

JVM vòng lên số của bạn và thay đổi nó thành:

1.1000000000000001e-8 
Các vấn đề liên quan