2012-07-16 68 views
5

可能重複:
Retain precision with Doubles in javaJava的雙精度恆定乘法/除法

你知道在Java中這兩個操作之間的差異。

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

我在生成的字節碼中看到m2的預編譯結果已經不是我所期望的了。
javap -verbose -c輸出I可以看到下面的值:

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

當我使用在其他表達式M1或M2,我不具有相同的結果。

當我嘗試用C同樣的事情,M1和M2是嚴格1.1E-8

我想我的問題是在Java的方式處理雙精度計算的,但我無法解釋自己,我錯過了。

回答

1

的你的區別在於1e9是完全可代表的,1e-9不完全可以代表。一個小的表示錯誤會導致一個算術錯誤,您可以看到。

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

打印

1000000000 
1.0000000000000000622815914577798564188970686927859787829220294952392578125E-9 

的區別不在於你是否使用*/,但你已經使用在一種情況下的整體數量和其他一小部分。

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

打印

1.1E10 
1.1000000000000001E-8 
+0

好吧,這是Java的解釋,但現在爲什麼當我在C中執行相同的代碼我有我需要的所有精度? – Joker 2012-07-16 12:17:10

+0

C編譯器將比* javac更進一步優化代碼。這可能意味着它可以將'/ 1e-9'轉換成'* 1e9',因爲a)它更快,b)更準確。Java的編譯器只做很少的優化,這留給了JIT,這也意味着未優化和優化的代碼必須做同樣的事情,否則在程序運行時你會看到行爲的改變。在C和Java中,將'double'轉換爲String的方式不同,但不太可能成爲差異的原因。在這兩種情況下,他們都使用相同的FPU。 – 2012-07-16 12:20:30

+1

此外,C編譯器可以使用您的計算機具有的80位浮點寄存器/計算。這是平臺相關的,只能在x86/x64處理器上使用。 Java將在每個平臺上表現相同,因此即使80位浮點可用,也只會使用64位浮點。 – 2012-07-16 12:23:22

3

Java和C之間沒有區別,都是IEEE浮點標準。唯一的區別可能是您打印它的方式。確切的數字1.1e-8不能表示爲double。輸出的差異可能歸因於關於如何累積表示的誤差的細節,以及用什麼舍入來打印結果。

+0

是但其分割爲1〜10^9 – Pooya 2012-07-16 11:50:39

+0

我認爲差別只在多少誤差被積累,其中,所述第二線僅有幾步超過打印在閾值。 – 2012-07-16 11:53:44

0

如果您使用的BigDecimal如下:

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

,你會看到你的號碼是

1.10000000000000006850975060355784206078677556206457666121423244476318359375E-8 

JVM圍捕你的號碼,將其更改爲:

1.1000000000000001e-8