Cara yang benar untuk membandingkan System. Double to '0' (a number, int?)

87

Maaf, ini mungkin pertanyaan bodoh yang mudah, tapi saya perlu tahu untuk memastikannya.

Saya memiliki ifekspresi ini ,

void Foo()
{
    System.Double something = GetSomething();
    if (something == 0) //Comparison of floating point numbers with equality 
                     // operator. Possible loss of precision while rounding value
        {}
}

Apakah ungkapan itu sama dengan

void Foo()
{
    System.Double something = GetSomething();
    if (something < 1)
        {}
}

? Karena saya mungkin memiliki masalah, memasukkan ifdengan misalnya nilai 0,9.

radbyx
sumber
3
// Comparison of floating point numbers with equality // operator. Apakah Anda benar-benar perlu menjelaskannya? :)
George Johnston
1
Tidak. Ada banyak sekali nilai antara 0 dan 1. Mengapa tidak mengujinya dan melihat sendiri?
Igby Largeman
12
Saya hanya menulis hal yang sama seperti yang dilakukan Resharper, untuk menunjukkan di mana fokus saya.
radbyx
@Charles: Juga, ada banyak angka yang kurang dari 0.
Brian
kemungkinan duplikat Membandingkan nilai ganda di C #
Dzyann

Jawaban:

116

Nah, seberapa dekat Anda membutuhkan nilainya dengan 0? Jika Anda melalui banyak operasi floating point yang dalam "presisi tak terbatas" mungkin menghasilkan 0, Anda bisa mendapatkan hasil "sangat dekat" dengan 0.

Biasanya dalam situasi ini Anda ingin memberikan semacam epsilon, dan memeriksa apakah hasilnya tepat di dalam epsilon itu:

if (Math.Abs(something) < 0.001)

Epsilon yang harus Anda gunakan adalah khusus aplikasi - itu tergantung pada apa yang Anda lakukan.

Tentu saja, jika hasilnya benar - benar nol, maka pemeriksaan persamaan sederhana sudah cukup.

Jon Skeet
sumber
Sebenarnya, saya ingin angka itu tepat nol, bagaimana kelihatannya? Saya mencari Double.Zero, tapi tahu keberuntungan. Ada konstanta kan? Btw, terima kasih, saya mendapatkan bagian epsilon sekarang :)
radbyx
24
@radbyx: Cukup gunakan == 0. Anda punya literal di sana - itu cukup konstan :)
Jon Skeet
35

Jika somethingtelah ditetapkan dari hasil operasi selain dari something = 0maka Anda lebih baik menggunakan:

if(Math.Abs(something) < Double.Epsilon)
{
//do something
}

Edit : Kode ini salah. Epsilon adalah angka terkecil, tetapi tidak cukup nol. Ketika Anda ingin membandingkan suatu angka dengan angka lain, Anda perlu memikirkan toleransi yang dapat diterima. Katakanlah apa pun di luar 0,00001 yang tidak Anda pedulikan. Itu nomor yang Anda gunakan. Nilainya bergantung pada domain. Namun, sebagian besar pasti tidak pernah Double.Epsilon.

sonatique.dll
sumber
2
Ini tidak memecahkan masalah pembulatan, misalnya Math.Abs(0.1f - 0.1d) < double.Epsilonadalahfalse
Thomas Lule
6
Double.Epsilon terlalu kecil untuk perbandingan seperti itu. Double.Epsilon adalah bilangan positif terkecil yang dapat diwakili oleh double.
Evgeni Nabokov
3
Ini tidak masuk akal karena secara praktis sama dengan membandingkan dengan 0. -1
Gaspa79
1
Tujuannya adalah untuk membandingkan konsep 0 tanpa menggunakan ==. Setidaknya itu masuk akal secara matematis. Saya berasumsi Anda memiliki dobel di tangan dan Anda ingin membandingkannya dengan konsep nol tanpa ==. Jika ganda Anda berbeda dari 0d karena alasan apa pun, termasuk pembulatan, tes isian mengembalikan salah. Perbandingan ini tampaknya valid untuk setiap double dan akan mengembalikan true hanya jika double ini lebih kecil dari angka terkecil yang dapat direpresentasikan, yang tampaknya merupakan definisi yang baik untuk menguji konsep 0, bukan?
sonatique
3
@MaurGi: Anda salah: double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); }
sonatique
27

Anda somethingadalah a double, dan Anda telah mengidentifikasinya dengan benar di baris

if (something == 0)

kita memiliki a doubledi sisi kiri (lhs) dan intdi sisi kanan (rhs).

Tapi sekarang sepertinya Anda berpikir lhs akan diubah menjadi int, dan kemudian ==tanda akan membandingkan dua bilangan bulat. Itu bukan apa yang terjadi. Konversi dari double ke int adalah eksplisit dan tidak dapat terjadi "otomatis".

Sebaliknya, yang terjadi justru sebaliknya. Rhs diubah menjadi double, dan kemudian ==tanda itu menjadi uji kesetaraan antara dua ganda. Konversi ini implisit (otomatis).

Menulis dianggap lebih baik (oleh beberapa orang)

if (something == 0.0)

atau

if (something == 0d)

karena Anda akan segera membandingkan dua ganda. Namun, itu hanya masalah gaya dan keterbacaan karena kompilator akan melakukan hal yang sama dalam hal apa pun.

Juga relevan, dalam beberapa kasus, untuk memperkenalkan "toleransi" seperti dalam jawaban Jon Skeet, tetapi toleransi itu juga akan menjadi double. Ini bisa saja menjadi 1.0jika Anda ingin, tetapi tidak harus [paling ketat positif] integer.

Jeppe Stig Nielsen
sumber
17

Jika Anda hanya ingin menekan peringatan tersebut, lakukan ini:

if (something.Equals(0.0))

Tentu saja, ini hanya solusi yang valid jika Anda tahu bahwa drift bukanlah masalah. Saya sering melakukan ini untuk memeriksa apakah saya akan membagi dengan nol.

Russell Phillips
sumber
4

Sejujurnya, saya tidak berpikir itu sama. Pertimbangkan contoh Anda sendiri: sesuatu = 0,9, atau 0,0004. Dalam kasus pertama akan menjadi SALAH, dalam kasus kedua akan BENAR. Berurusan dengan jenis ini saya biasanya menentukan untuk saya persentase presisi dan membandingkan dalam presisi itu. Tergantung kebutuhan Anda. sesuatu seperti...

if(((int)(something*100)) == 0) {


//do something
}

Semoga ini membantu.

Tigran
sumber
2
sesuatu harus benar-benar nol.
radbyx
jadi Anda orang yang beruntung, dalam hal ini :)
Tigran
3

Berikut adalah contoh masalah (disiapkan di LinQPad - jika Anda tidak memilikinya, gunakan saja Console.Writelinealih-alih Dumpmetode):

void Main()
{
    double x = 0.000001 / 0.1;
    double y = 0.001 * 0.01; 

    double res = (x-y);
    res.Dump();
    (res == 0).Dump();
}

Baik x dan y secara teoritis sama dan sama dengan: 0,00001 tetapi karena kurangnya "presisi tak terbatas", nilai-nilai tersebut sedikit berbeda. Sayangnya cukup sedikit untuk kembali falsesaat membandingkan ke 0 dengan cara biasa.

michal-mad
sumber