2012-02-01 17 views
16

Ho un metodo in java che restituisce un doppio numero e voglio confrontare ogni doppio numero che viene restituito ogni volta che chiamo il metodo (diciamo 5 volte), in modo che possa concludere che il numero restituito è quasi lo stesso ogni tempo.Come confrontare quella sequenza di doppi sono tutti "approssimativamente uguali" in Java?

Come posso fare questo?

+2

si può dire di più sul contesto? Stai facendo questo da un test unitario? In tal caso, uno degli assertEquals in JUnit ha proprio un tale metodo per affermare che due doppi sono vicini. –

+0

Grazie a tutti per le risposte. Quindi sì, sto provando a fare un test delle unità, e il mio metodo restituisce il tempo in cui alcuni numeri interi vengono aggiunti a un array. Voglio testare questo metodo con diverse quantità di numeri interi e confrontare i tempi. Immagino che il metodo ulp() possa funzionare. – FranXh

risposta

6

È necessario prima decidere cosa significa "quasi lo stesso". Ad esempio, c'è un metodo in java.lang.Math chiamato ulp() che, dato un doppio, restituisce la distanza tra quel doppio e il successivo; cioè, la minima differenza possibile tra quel numero e qualsiasi altro. Potresti semplicemente confrontare la differenza tra i due doppi e il risultato di chiamare quel metodo.

D'altra parte, forse si desidera che due numeri si trovino solo nell'1% l'uno dell'altro. In tal caso, eseguire lo stesso calcolo, ma utilizzare il primo numero moltiplicato per 0.01 anziché ulp() come distanza accettabile maggiore.

+0

Grazie a tutti per le risposte. Quindi sì, sto provando a fare un test delle unità, e il mio metodo restituisce il tempo in cui alcuni numeri interi vengono aggiunti a un array. Voglio testare questo metodo con diverse quantità di numeri interi e confrontare i tempi. Immagino che il metodo ulp() possa funzionare. – FranXh

6

L'uguaglianza approssimativa è definita in termini di differenza assoluta: se una differenza assoluta non supera un certo numero presumibilmente piccolo, allora si può dire che i valori che si stanno confrontando sono "abbastanza vicini".

double diff = Math.abs(actual - expected); 
if (diff < 1E-7) { 
    // Numbers are close enough 
} 

È necessario essere molto attenti a non confondere "abbastanza vicino" fine "uguale", perché i due sono fondamentalmente diversi: l'uguaglianza è transitiva (b cioè un == e b == c insieme implica che a = = c), mentre "abbastanza vicino" non è transitivo.

1

Che cosa significa per due doppi essere "approssimativamente uguali?" Significa che i doppi sono all'interno di una certa tolleranza l'uno dell'altro. La dimensione di tale tolleranza e se tale tolleranza è espressa come numero assoluto o percentuale dei due doppi, dipende dalla tua applicazione.

Ad esempio, due foto visualizzate su un visualizzatore di foto hanno approssimativamente la stessa larghezza in pollici se occupano lo stesso numero di pixel sullo schermo, quindi la tolleranza sarà un numero assoluto calcolato in base alla dimensione dei pixel per lo schermo . D'altro canto, i profitti di due società finanziarie sono probabilmente "approssimativamente uguali" se si trovano nello 0,1% l'uno dell'altro. Questi sono solo esempi ipotetici, ma il punto è che dipende dalla tua applicazione.

Ora per qualche implementazione. Supponiamo che la tua applicazione richieda una tolleranza assoluta. Quindi è possibile utilizzare

private static final double TOLERANCE = 0.00001; 

public static boolean approxEqual(final double d1, final double d2) { 
    return Math.abs(d1 - d2) < TOLERANCE; 
} 

per confrontare due doppie, e utilizzare

approxEqual(d1, d2) && approxEqual(d1, d3) && approxEqual(d1, d4) && approxEqual(d1, d5) 

per confrontare cinque doppie.

26
public static boolean almostEqual(double a, double b, double eps){ 
    return Math.abs(a-b)<eps; 
} 

Dove eps è misura di uguaglianza.

+0

Mi sono permesso di rimuovere un extra ')' –

+2

'Math.abs ((ab)/a) sffc

4

Dipende da cosa intendi per simile. Se vuoi confrontare due numeri all'interno di un errore assoluto, ad es. 1e-6 puoi usare epsilon. Se si desidera confrontare due double indipendentemente dalla scala. per esempio. 1.1e-20 e 1.3e-20 non sono simili ma 1.1e20 e 1.1e20 + 1e5 puoi confrontare il valore grezzo.

public static void main(String... args) throws IOException { 
    test(1.1e-20, 1.3e-20); 
    test(1.1e20, 1.1e20 + 1e5); 
} 

private static void test(double a, double b) { 
    System.out.println(a + " and " + b + ", similar= " + similarUnscaled(a, b, 10)); 
} 

public static boolean similarUnscaled(double a, double b, long representationDifference) { 
    long a2 = Double.doubleToRawLongBits(a); 
    long b2 = Double.doubleToRawLongBits(b); 
    // avoid overflow in a2 - b2 
    return ((a2 >= 0) == (b2 >= 0)) && 
      Math.abs(a2 - b2) <= representationDifference; 
} 

stampe

1.1E-20 and 1.3E-20, similar= false 
1.1E20 and 1.100000000000001E20, similar= true 
+1

Attento con il modello comune di confrontare una differenza con 0 con questo. Intorno allo 0 diventa improvvisamente molto sensibile: 'SimilarUnscaled (0.69/3, 0.23, 10)! = SimilarUnscaled (0.69/3 - 0.23, 0, 10)' – georg

Problemi correlati