2010-09-16 69 views
21

以下是以下程序的輸出。執行雙值相等比較時應該是什麼ε值

value is : 2.7755575615628914E-17 
Double.compare with zero : 1 
isEqual with zero : true 

我的問題是,應該是什麼的ε值?有沒有強大的方式來獲得價值,而不是從天空中挑選一個數字。


package sandbox; 

/** 
* 
* @author yccheok 
*/ 
public class Main { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     double zero = 1.0/5.0 + 1.0/5.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0; 
     System.out.println("value is : " + zero); 
     System.out.println("Double.compare with zero : " + Double.compare(zero, 0.0)); 
     System.out.println("isEqual with zero : " + isEqual(zero, 0.0)); 
    } 

    public static boolean isEqual(double d0, double d1) { 
     final double epsilon = 0.0000001; 
     return d0 == d1 ? true : Math.abs(d0 - d1) < epsilon; 
    } 
} 
+1

@Cheok,你的問題不清楚,你對epsilon有什麼期待? – mhshams 2010-09-16 15:48:19

回答

8

第二個問題的答案是否定的。有限機器精度誤差的大小可以任意大:

public static void main(String[] args) { 
    double z = 0.0; 
    double x = 0.23; 
    double y = 1.0/x; 
    int N = 50000; 
    for (int i = 0; i < N; i++) { 
     z += x * y - 1.0; 
    } 
    System.out.println("z should be zero, is " + z); 
} 

這給~5.55E-12,但如果增加N你可以只是你的願望錯誤的任何水平。

關於如何編寫數值穩定算法,有大量過去和現在的研究。這是一個難題。

+0

0.23是一個幻數嗎? – 2010-09-16 16:20:23

+0

不,只是一個數字的例子,其中x *(1.0/x)不完全等於1. – mob 2010-09-16 16:59:18

+1

難道我們不應該將0.23作爲「幻數」嗎?因爲我也可以使用0.24,對吧?我想如果我們可以選擇一個任意數字,我們通常稱它爲「魔術數字」。 – 2010-09-18 06:10:00

7

沒有一個正確的價值。您需要根據涉及的數量的大小來計算它。你基本上處理的是一些有效數字,而不是一個特定的數值。例如,如果你的數字都在1e-100的範圍內,並且你的計算應該保持大約8位有效數字,那麼你的epsilon應該在1e-108左右。如果您對1e + 200範圍內的數字進行了相同的計算,那麼您的epsilon將在1e + 192附近(即epsilon = magnitude - 有效數字)。

我還會注意到isEqual是一個可憐的名字 - 你想要的東西像isNearlyEQual。出於一個原因,人們相當合理地認爲「平等」是傳遞性的。至少,您需要傳達結果不再是傳遞的想法 - 即,即使isEqual(a, b)isEqual(b, c)均爲真,您的isEqual,isEqual(a, c)的定義可能是錯誤的。

編輯:(迴應評論):我說:「如果你的計算應該保持大約8位有效數字,那麼你的epsilon應該是......」。基本上,它要考慮你正在做什麼計算,以及你在這個過程中可能失去多少準確性,以提供一個合理的猜測,以確定它在重大之前有多大的差異。不知道你在做什麼計算,我無法猜測。

只要epsilon的幅度爲:不,它確實是而不是對於它總是小於或等於1是有意義的。浮點數只能保持有限的精度。在IEEE雙精度浮點的情況下,可以表示的最大精度爲大約20位十進制數字。這意味着如果你從1e + 200開始,那麼機器可以代表的所有與該機器所代表的之間絕對最小的差值約爲1e + 180(而double可以表示最大值爲〜1e + 308的數字,此時最小可以表示的差異是〜1e + 288)。

+0

爲什麼我的號碼是1e-100,那麼epsilon應該在1e-108左右。爲什麼8位有效數字? – 2010-09-16 16:18:31

+0

@Coffin,爲什麼epsilon是1e + 192? epsilon是不是應該至少小於1,大於0? – 2010-09-16 16:19:38

11

我喜歡(僞代碼,我不這樣做JAVA)

bool fuzzyEquals(double a, double b) 
{ 
    return abs(a - b) < eps * max(abs(a), abs(b)); 
} 

與小量是幾十倍的機器精度。如果您不知道要使用什麼,請取10^-12。

然而,這是相當依賴於問題。如果給出a和b的計算容易出現舍入誤差,或涉及許多操作,或者它們本身處於某種(已知)精度範圍內,則需要採取更大的ε。

這一點是使用相對精度,不是絕對的。

+0

我可以返回abs(a - b) 2010-09-16 16:31:00

+0

@Yan:當然可以。 – 2010-09-16 16:34:54

+0

我認爲我們應該在abs(a)和abs(b)之間進行比較,並取最小值與eps相乘。看到本質上等於:http://jstock.cvs.sourceforge.net/viewvc/jstock/jstock/src/org/yccheok/jstock/portfolio/Utils.java?revision=1.15&view=markup – 2010-09-18 06:08:28

1

你應該首先閱讀https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

它討論了各種比較浮點數的方法:絕對公差,相對公差,ulp距離。這是一個相當好的論點,ulp檢查是一條路。這個事例圍繞着這個觀點,即如果你想檢查兩個浮點數是否相同,你必須考慮可表示浮點數之間的距離。換句話說,你應該檢查兩個數字是否在彼此的浮動內。

該算法在C中給出,但可以使用java.lang.Double#doubleToLongBitsjava.lang.Float#floatToIntBits將其轉換爲java來實現從浮點型轉換爲整型類型。另外,對於java> 1.5,有方法ulp(double)ulp(float),對於java> 1.6 nextUp(double)nextUp(float)nextAfter(double, double)nextAfter(float, float)對於量化兩個浮點數之間的差異很有用。

+0

該文章指出它已過時,並且這是替換: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ – clausavram 2017-12-20 17:20:27

1

有兩個概念這裏涉及:

  1. 一種機器精度單位:Double.ulp()
  2. 的機器精度對於給定的double dDouble.ulp(d)

如果您致電Double.ulp()您將獲得機器精度單位,這是您可以從某個硬件平臺期望的精度......無論這個定義可能是什麼!

如果您撥打Double.ulp(d),您將獲得機器精度爲double d。換句話說,每個double d都有其特定的精度。這比前一段更有用。

當您執行涉及級聯計算的迭代時,您必須特別注意細節,即:當前計算中採用先前計算的結果時。這是因爲在這些情況下積累了錯誤,並且在某些情況下可能會導致結果遠離他們應該提供的真實價值。在某些情況下,累計誤差的大小甚至可能大於真實值。看到一些disastrous examples here

在某些業務領域,數值計算錯誤根本無法接受。根據業務領域,其規定,要求和特性,您必須採用其他方法來簡化浮點算法的選擇(即doublesfloats)。

在財務的情況下,例如,永遠不會使用浮點運算。在處理金錢時千萬不要使用doublesfloats。決不。期。根據具體情況,您可以採用BigDecimal或fixed point arithmetic

在處理股票價格的具體情況下,您知道價格總是有5位數字的精度,在這種情況下,fixed point arithmetic足夠大,並且可以提供您可能獲得的最大性能,這是一個非常強大的這個商業領域的共同要求。

如果業務領域確實需要數值計算,那麼在這種情況下,您必須確保在嚴格和非常謹慎的控制下保持錯誤傳播。這是一個很長的主題,有很多技巧,開發人員經常忽視這個問題,只是簡單地相信有一個魔法調用方法可以爲他們完成所有的辛苦工作。不,它沒有。你必須做好你的研究,做好功課,做好所有必要的努力工作,以確保你控制錯誤。您需要準確理解您實施的數值算法會發生什麼。