Arti argumen epsilon dari assertEquals untuk nilai ganda

187

Saya punya pertanyaan tentang junit assertEqualsuntuk menguji nilai ganda. Membaca dokumen API saya bisa melihat:

@Deprecated
public static void assertEquals(double expected, double actual)

Usang. Gunakan assertEquals sebagai gantinya

Apa artinya epsilonnilainya? (Epsilon adalah huruf dalam alfabet Yunani, kan?).

Adakah yang bisa menjelaskan kepada saya bagaimana cara menggunakannya?

Édipo Féderle
sumber

Jawaban:

198

Epsilon adalah nilai yang bisa dimatikan oleh 2 angka. Jadi itu akan menegaskan kebenaran selamaMath.abs(expected - actual) < epsilon

Jberg
sumber
3
Jadi, nilai apa yang harus saya berikan sebagai epsilon?
emeraldhieu
15
@ Emerald214 jumlah presisi. Jika Anda ingin menegaskan bahwa nilai ganda adalah 0D epsilon akan menjadi 0 (akurasi 100%, tidak ada pengecualian). Jika Anda menginginkan margin kesalahan (katakan untuk derajat), Anda dapat mengatur epsilon ke 1 yang berarti bahwa, misalnya, 64.2 ° sama dengan 64.8 ° (karena abs (64.8-64.2) <1)
Pieter De Bie
3
Dokumentasi mengatakan, "delta - delta maksimum antara yang diharapkan dan aktual yang kedua bilangannya dianggap sama." Jadi saya pikir itu seharusnya <=tidak <.
Andrew Cheong
Melihat kode, saya melihatnya memanggil metode doubleIsDifferent(untuk membandingkan nilai ganda) dan mengembalikannya Math.abs(d1 - d2) > delta. Jadi jika perbedaan antara d1 dan d2 lebih tinggi dari delta, itu berarti nilainya berbeda dan akan mengembalikan true. Ini akan mengembalikan false jika nilainya dianggap sama. Metode itu disebut dalam assertEquals secara langsung dan jika itu mengembalikan nilai true, maka assertEquals akan memanggil failNotEqualsdan hasil pengujian akan gagal.
anthomaxcool
1
@ jbert Adakah yang bisa menyarankan nilai ganda epsilon tipikal jika saya hanya bekerja dengan rata-rata banyak angka atau melakukan standar deviasi?
simgineer
121

Versi JUnit manakah ini? Saya hanya pernah melihat delta, bukan epsilon - tapi itu masalah sampingan!

Dari jUnit javadoc :

delta - delta maksimum antara yang diharapkan dan aktual yang kedua bilangannya masih dianggap sama.

Ini mungkin berlebihan, tetapi saya biasanya menggunakan jumlah yang sangat kecil, misalnya

private static final double DELTA = 1e-15;

@Test
public void testDelta(){
    assertEquals(123.456, 123.456, DELTA);
}

Jika Anda menggunakan pernyataan hamcrest , Anda bisa menggunakan standar equalTo()dengan dua ganda (tidak menggunakan delta). Namun jika Anda ingin delta, Anda bisa menggunakan closeTo()(lihat javadoc ), mis

private static final double DELTA = 1e-15;

@Test
public void testDelta(){
    assertThat(123.456, equalTo(123.456));
    assertThat(123.456, closeTo(123.456, DELTA));
}

FYI JUnit 5 mendatang juga akan membuat delta opsional saat memanggil assertEquals()dengan dua ganda. The pelaksanaan (jika Anda tertarik) adalah:

private static boolean doublesAreEqual(double value1, double value2) {
    return Double.doubleToLongBits(value1) == Double.doubleToLongBits(value2);
}
James Bassett
sumber
57

Perhitungan floating point tidak tepat - sering ada kesalahan pembulatan, dan kesalahan karena representasi. (Misalnya, 0,1 tidak dapat secara tepat direpresentasikan dalam titik mengambang biner.)

Karena itu, secara langsung membandingkan dua nilai floating point untuk kesetaraan biasanya bukan ide yang baik, karena mereka dapat berbeda dengan jumlah kecil, tergantung pada bagaimana mereka dihitung.

"Delta", seperti yang disebut dalam javadocs JUnit , menjelaskan jumlah perbedaan yang dapat Anda toleransi dalam nilai agar mereka dianggap sama. Ukuran nilai ini sepenuhnya tergantung pada nilai yang Anda bandingkan. Saat membandingkan ganda, saya biasanya menggunakan nilai yang diharapkan dibagi 10 ^ 6.

mdma
sumber
11

Masalahnya adalah bahwa dua ganda mungkin tidak persis sama karena masalah presisi yang melekat pada angka floating point. Dengan nilai delta ini, Anda dapat mengontrol evaluasi kesetaraan berdasarkan faktor kesalahan.

Juga beberapa nilai floating-point dapat memiliki nilai khusus seperti NAN dan -Infinity / + Infinity yang dapat mempengaruhi hasil.

Jika Anda benar-benar berniat untuk membandingkan bahwa dua ganda sama persis, lebih baik membandingkannya sebagai representasi panjang

Assert.assertEquals(Double.doubleToLongBits(expected), Double.doubleToLongBits(result));

Atau

Assert.assertEquals(0, Double.compareTo(expected, result));

Yang bisa memperhitungkan nuansa ini.

Saya belum mempelajari metode Assert yang dipermasalahkan, tetapi saya hanya bisa berasumsi bahwa sebelumnya sudah tidak digunakan lagi untuk masalah seperti ini dan yang baru memperhitungkannya.

Edwin Dalorzo
sumber
2

Epsilon adalah perbedaan antara expecteddan actualnilai - nilai yang dapat Anda terima dengan menganggap mereka setara. Anda dapat mengatur .1misalnya.

Konstantiner
sumber
2

Perhatikan bahwa jika Anda tidak melakukan matematika, tidak ada yang salah dengan menyatakan nilai floating point yang tepat. Misalnya:

public interface Foo {
    double getDefaultValue();
}

public class FooImpl implements Foo {
    public double getDefaultValue() { return Double.MIN_VALUE; }
}

Dalam hal ini, Anda ingin memastikan itu benar-benar MIN_VALUE, bukan nol atau -MIN_VALUEatau MIN_NORMALatau nilai yang sangat kecil lainnya. Bisa dibilang begitu

double defaultValue = new FooImpl().getDefaultValue();
assertEquals(Double.MIN_VALUE, defaultValue);

tetapi ini akan memberi Anda peringatan penghentian. Untuk menghindarinya, Anda bisa menelepon assertEquals(Object, Object):

// really you just need one cast because of autoboxing, but let's be clear
assertEquals((Object)Double.MIN_VALUE, (Object)defaultValue);

Dan, jika Anda benar-benar ingin terlihat pintar:

assertEquals(
    Double.doubleToLongBits(Double.MIN_VALUE), 
    Double.doubleToLongBits(defaultValue)
);

Atau Anda bisa menggunakan pernyataan gaya lancar Hamcrest:

// equivalent to assertEquals((Object)Double.MIN_VALUE, (Object)defaultValue);
assertThat(defaultValue, is(Double.MIN_VALUE));

Jika nilai Anda memeriksa sedang tidak datang dari melakukan beberapa matematika, meskipun, menggunakan epsilon tersebut.

David Moles
sumber
7
Jika Anda ingin memeriksa yang sama persis, set epsilon ke 0,0 - varian Obyek tidak diperlukan.
Mel Nicholson
-2
Assert.assertTrue(Math.abs(actual-expected) == 0)
Prakash
sumber
Saat menggunakan angka floating point (seperti float atau double), ini tidak akan bekerja dengan andal. Anda mungkin ingin meninjau bagaimana angka floating point di java disimpan dan bagaimana operasi aritmatika bekerja pada mereka. (spoiler: mengharapkan beberapa kesalahan pembulatan!)
Attila