Bir System.Double ile '0'ı karşılaştırmanın doğru yolu (sayı, int?)


87

Üzgünüm, bu kolay aptalca bir soru olabilir ama emin olmak için bilmem gerekiyor.

Bu ififadeye sahibim

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

Bu ifade eşit mi

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

? Çünkü o zaman bir problemim olabilir, iförneğin 0,9 değerini girerek .


3
// Comparison of floating point numbers with equality // operator. Bunu gerçekten belirtmeniz gerekiyor mu? :)
George Johnston

1
Heck hayır. 0 ile 1 arasında çok fazla değer var. Neden sadece test edip kendiniz görmüyorsunuz?
Igby Largeman

12
Odağımın nerede olduğunu göstermek için Resharper'ın yaptığı gibi yazdım.
radbyx

@Charles: Ayrıca, 0'dan küçük pek çok sayı var.
Brian

Yanıtlar:


116

Peki, değerin 0'a ne kadar yakın olması gerekiyor? "Sonsuz hassasiyette" 0 ile sonuçlanabilecek çok sayıda kayan nokta işleminden geçerseniz, 0'a "çok yakın" bir sonuç elde edebilirsiniz.

Tipik olarak bu durumda bir çeşit epsilon sağlamak ve sonucun bu epsilon içinde olup olmadığını kontrol etmek istersiniz:

if (Math.Abs(something) < 0.001)

Kullanmanız gereken epsilon uygulamaya özeldir - ne yaptığınıza bağlıdır.

Elbette, sonuç tam olarak sıfırsa, basit bir eşitlik kontrolü yeterlidir.


Aslında tam olarak sıfır olmasına ihtiyacım var, bu nasıl görünür? Double.Zero'yu aradım ama şansı biliyorum Sabit bir hak mı var? Btw, teşekkürler, epsilon kısmını şimdi
alıyorum

24
@radbyx: Sadece kullan == 0. Orada gerçek bir bilginiz var - bu oldukça sabit :)
Jon Skeet

35

Başka somethingbir işlemin sonucundan atanmışsa, şunu something = 0kullanmanız daha iyi olur:

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

Düzenleme : Bu kod yanlış. Epsilon en küçük sayıdır, ancak tam olarak sıfır değildir. Bir sayıyı başka bir sayı ile karşılaştırmak istediğinizde, kabul edilebilir toleransın ne olduğunu düşünmeniz gerekir. .00001 dışında umursamadığınız bir şey diyelim. Kullanacağın sayı bu. Değer, alana bağlıdır. Ancak, çoğunlukla kesinlikle Double.Epsilon değildir.


2
Bu, örneğin, yuvarlama sorunu çözmez Math.Abs(0.1f - 0.1d) < double.Epsilonisefalse
Thomas Lüle

6
Double.Epsilon böyle bir karşılaştırma için çok küçük. Double.Epsilon, çiftin temsil edebileceği en küçük pozitif sayıdır.
Evgeni Nabokov

3
Bu mantıklı değil çünkü pratikte
0-1 ile

1
Amaç, == kullanmadan 0 kavramıyla karşılaştırmaktır. En azından matematiksel olarak anlamlandırıyor. Elinizde bir çiftiniz olduğunu ve bunu == olmadan sıfır kavramıyla karşılaştırmak istediğinizi varsayıyorum. İkiliniz yuvarlama dahil herhangi bir nedenle 0d'den farklıysa, test dolgusu yanlış döndürür. Bu karşılaştırma herhangi bir çift için geçerli görünüyor ve ancak bu çift temsil edilebilecek en küçük sayıdan en küçükse doğru dönecektir, bu 0 kavramını test etmek için iyi bir tanım gibi görünüyor, değil mi?
sonatique

3
@MaurGi: yanılıyorsun: double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); }
sonatique

27

Siz somethingbir doubleve satırda bunu doğru bir şekilde tanımladınız

if (something == 0)

Biz var doublesol tarafta (sol eksen) ve intsağ taraftaki (rhs) üzerine.

Ama şimdi lhs'nin an'a dönüştürüleceğini düşünüyorsunuz intve sonra ==işaret iki tamsayıyı karşılaştıracak. Olan bu değil . Dönüşüm gelen double etmek int olduğunu açıkça ve "otomatik olarak" olamaz.

Bunun yerine tam tersi olur. Rhs'ye dönüştürülür doubleve ardından ==işaret iki çift arasında bir eşitlik testi haline gelir. Bu dönüştürme örtüktür (otomatik).

Yazmak (bazıları tarafından) daha iyi kabul edilir

if (something == 0.0)

veya

if (something == 0d)

çünkü o zaman hemen iki dublörü karşılaştırıyorsunuz. Ancak, bu sadece bir stil ve okunabilirlik meselesi çünkü derleyici her durumda aynı şeyi yapacaktır.

Bazı durumlarda, Jon Skeet'in cevabında olduğu gibi bir "hoşgörü" sunmak da önemlidir, ancak bu hoşgörü de geçerli olacaktır double. Bu olabilir tabii olmak 1.0isterseniz, ancak böyle olmak [az kesinlikle olumlu] tamsayı yok.


17

Sadece uyarıyı bastırmak istiyorsanız, şunu yapın:

if (something.Equals(0.0))

Elbette, bu yalnızca sürüklenmenin bir sorun olmadığını biliyorsanız geçerli bir çözümdür. Bunu genellikle sıfıra bölmek üzere olup olmadığımı kontrol etmek için yaparım.


4

Dürüst olmak gerekirse, bunun eşit olduğunu düşünmüyorum. Kendi örneğinizi düşünün: bir şey = 0.9 veya 0.0004. İlk durumda YANLIŞ, ikinci durumda DOĞRU olacaktır. Bu türlerle uğraşırken genellikle benim için kesinlik yüzdesini tanımlarım ve bu hassasiyet içinde karşılaştırırım. İhtiyaçlarınıza bağlıdır. gibi bir şey...

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


//do something
}

Bu yardımcı olur umarım.


2
bir şey tam olarak sıfır olmalıdır.
radbyx

bu yüzden şanslı bir adamsın, bu durumda :)
Tigran

3

Problemi gösteren örnek aşağıdadır (LinQPad'de hazırlanmıştır - eğer yoksa sadece metot Console.Writelineyerine kullanın Dump):

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

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

Hem x hem de y teorik olarak aynıdır ve şuna eşittir: 0,00001, ancak "sonsuz hassasiyet" eksikliğinden dolayı bu değerler biraz farklıdır. Maalesef falsenormal şekilde 0 ile karşılaştırırken dönmek için biraz yeterli .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.