2009-10-01 21 views

回答

26

這是因爲距離1.3最近的浮點值與距離1.3最近的double值不同。這兩個值都不會是,而是 1.3 - 不能完全用非循環二進制表示法表示。

舉個爲什麼出現這種情況有不同的理解,假設我們有兩個小數浮點類型 - decimal5decimal10,其中的數字代表顯著位數。現在假設我們試圖將「三分之一」的值分配給它們兩個。你會最終與

decimal5 oneThird = 0.33333 
decimal10 oneThird = 0.3333333333 

顯然這些值是不相等的。這裏的東西完全一樣,只是涉及不同的基地。

不過,如果你限制值的精確的少的類型,你會發現他們等於在這種特殊情況下

double d = 1.3d; 
float f = 1.3f; 
System.out.println((float) d == f); // Prints true 

這是不保證的情況下,但是。有時,從十進制文字到雙重表示的近似值,然後將該值近似爲浮點表示,最終結果不如直接十進制浮點近似精確。這1.0000001788139343的一個例子(感謝stephentyrone找到這個例子)。

Somewaht更安全,你可以做雙打之間的比較,但在原來的分配使用float文字:

double d = 1.3f; 
float f = 1.3f; 
System.out.println(d == f); // Prints true 

在後一種情況下,這是一個有點像說:

decimal10 oneThird = 0.3333300000 

但是,正如在評論中指出的,你幾乎可以肯定不應該比較浮點值與==。這幾乎是從來沒有正確的事情,因爲正是這樣的事情。通常,如果您想比較兩個值,您可以通過某種「模糊」平等比較來進行比較,請檢查這兩個數字是否足夠接近您的目的。有關更多信息,請參見Java Traps: double頁面。

如果您確實需要檢查絕對平等,那通常表示您應該首先使用不同的數字格式 - 例如,對於財務數據,您應該使用BigDecimal

+0

很不錯的解釋,雖然它是可能的一個精心製作的十進制值失敗的比較'((浮動)decimalValueAsDouble == decimalValueAsFloat)',因雙舍入。 –

+0

@ stephentyrone:是的,我懷疑它可能是 - 儘管我還沒有提出任何例子。 –

+1

這篇文章唯一讓我擔心的是,它給OP帶來的印象是,如果他不瞭解評論,他可以投射和使用==。 其他人發佈了這個鏈接:http://firstclassthoughts.co.uk/java/traps/java_double_traps.html這很好地解釋了它。 – Fredrik

2

從不檢查浮點數之間的相等性。具體來說,要回答你的問題,1.3的數字很難用二進制浮點表示,靈魂和浮點表示是不同的。

+0

如果你的帖子沒有填寫拼寫錯誤,你會從我那裏得到一個+1。 –

11

float是一個單精度浮點數。雙精度浮點數是雙精度浮點數。更多詳細信息,請訪問:http://www.concentric.net/~Ttwang/tech/javafloat.htm

注意:檢查浮點數的精確相等是個壞主意。大多數情況下,您想基於增量或容差值進行比較。

例如:

float a = 1.3f; 
double b = 1.3; 
float delta = 0.000001f; 
if (Math.abs(a - b) < delta) 
{ 
    System.out.println("Close enough!"); 
} 
else 
{ 
    System.out.println("Not very close!"); 
} 

一些數字可以不完全浮點(例如0.01),所以當你比較平等,你可能會得到意想不到的結果來表示。

2

閱讀this article

上面的文章清楚地說明了使用double和float類型時的例子。

2
float a=1.3f; 
double b=1.3; 

在這一點上,你有兩個變量包含實數1.3的二進制近似值。第一個近似值精確到約7位十進制數字,第二個近似值精確到約15位十進制數字。

if(a==b) { 

表達式a==b分兩個階段進行評估。首先通過填充二進制表示將a的值從float轉換爲double。作爲Real 1.3的表示,結果仍然只能精確到約7位十進制數字。接下來你比較兩個不同的近似值。由於它們不同,a==b的結果是false

有兩種吸取教訓:

  1. 浮點(雙)文字幾乎都是近似值;例如與文字1.3f對應的實際數字不完全等於實數1.3

  2. 每次進行浮點計算時,都會出現錯誤。這些錯誤往往會累積起來。所以當你比較浮點數/雙數時,使用簡單的「==」,「<」等等通常是錯誤的。相反,您應該使用|a - b| < delta,其中delta被適當選擇。 (和搞清楚什麼是適當的delta並不總是直截了當無論是。)

  3. 你應該採取的課程,數值分析:-)

+0

問題根本上在於,雖然有許多'double'值可以代表特定的'float'值,系統通過選擇一個特定的'double'值來執行'double'與'float'的比較。如果我有我的druthers,隱式double-to-float轉換通常會被允許,並且通常禁止隱式float-to-double,但浮點操作數只能與精確匹配的類型進行比較(有些情況下可能需要兩個操作數「浮動「,但其他人,」雙「;既不足以支持」安全「的默認)。 – supercat

0

的問題是Java(唉.NET也是一樣)關於float值是代表單個精確數字量還是數量範圍不一致。如果float被認爲代表Mant * 2^Exp的確切數字量,其中Mant是0到2^25的整數,並且Exp是整數),則嘗試將任何非該形式的數字轉換爲float應引發異常。如果它被認爲代表「在上述表格中的某些特定表示被認爲可能是最好的數字的軌跡」,那麼即使對於上述形式的double值,雙重浮動的轉換也是正確的[將最能代表數量的double鑄造成float將幾乎總是產生最能代表該數量的float,儘管在某些轉角情況下(例如8888888.500000000001至8888888.500000000932範圍內的數量),float選擇的數量可能是幾個零件兆比實際數量的最佳可能float表示更差]。

用一個比喻,假設兩個人都有一個十釐米長的物體,他們測量它。 Bob使用一套昂貴的口徑,並確定他的物體長3.937008英寸,Joe使用捲尺並確定他的物體長3 15/16英寸。這些物體的尺寸是否相同?如果將喬的測量結果轉換爲百萬分之一英寸(3.937500「),則測量結果會顯示爲不同的結果,但是將其測量結果轉換爲最接近1/256」的分數,它們看起來是相等的。雖然前者的比較可能看起來更「精確」,但後者比較有意義。喬的測量,如果3 15/16「並不真正意味着3.937500」 - 這意味着「使用捲尺測量,與3 15/16不可區分的距離」。和3.937008「一樣,就像喬的物體一樣,使用捲尺的距離與3 15/16是無法區分的。

不幸的是,儘管使用較低精度比較測量值會更有意義, Java的浮點比較規則假定一個float代表一個精確的數字量,並在此基礎上進行比較。雖然在某些情況下這是有用的(例如,知道是否通過將某些值轉換爲float並返回後生成的特定doubledouble將匹配起始值),通常在floatdouble之間的直接相等比較是沒有意義的。儘管Java不需要它,但是應該總是將浮點p的操作數等同比較爲相同類型。在比較之前將double轉換爲float的語義與將float轉換爲double的語法不同,並且默認情況下的Java拾取行爲(將float轉換爲double)通常在語義上是錯誤的。

相關問題