Przepraszam, to może być łatwe, głupie pytanie, ale muszę wiedzieć, aby mieć pewność.
Mam ten if
wyraz twarzy
void Foo()
{
System.Double something = GetSomething();
if (something == 0) //Comparison of floating point numbers with equality
// operator. Possible loss of precision while rounding value
{}
}
Czy to wyrażenie jest równe
void Foo()
{
System.Double something = GetSomething();
if (something < 1)
{}
}
? Bo wtedy mógłbym mieć problem wpisując if
np z wartością 0,9.
// Comparison of floating point numbers with equality // operator.
Czy naprawdę musiałeś to określić? :)Odpowiedzi:
Jak blisko chcesz, aby wartość była równa 0? Jeśli przejdziesz przez wiele operacji zmiennoprzecinkowych, które przy „nieskończonej precyzji” mogą dać 0, możesz otrzymać wynik „bardzo blisko” 0.
Zazwyczaj w tej sytuacji chcesz podać jakiś rodzaj epsilon i sprawdzić, czy wynik zawiera się właśnie w tym epsilonie:
if (Math.Abs(something) < 0.001)
Epsilon, którego powinieneś użyć, jest specyficzny dla aplikacji - zależy to od tego, co robisz.
Oczywiście, jeśli wynik powinien wynosić dokładnie zero, proste sprawdzenie równości jest w porządku.
źródło
== 0
. Masz tam dosłownie - to całkiem stałe :)Jeśli
something
został przypisany z wyniku innej operacji niżsomething = 0
to lepiej użyj:if(Math.Abs(something) < Double.Epsilon) { //do something }
Edycja : ten kod jest nieprawidłowy. Epsilon to najmniejsza liczba, ale nie całkiem zero. Jeśli chcesz porównać liczbę z inną liczbą, musisz pomyśleć o dopuszczalnej tolerancji. Powiedzmy, że nie obchodzi Cię coś poza 0,00001. To jest liczba, której użyjesz. Wartość zależy od domeny. Jednak z pewnością nigdy nie jest to Double.Epsilon.
źródło
Math.Abs(0.1f - 0.1d) < double.Epsilon
jestfalse
double d = Math.Sqrt(10100)*2; double a = Math.Sqrt(40400); if(Math.Abs(a - d) < double.Epsilon) { Console.WriteLine("true"); }
Twój
something
jest adouble
i poprawnie zidentyfikowałeś to w liniiif (something == 0)
mamy
double
po lewej stronie (lewa oś) iint
po prawej stronie (prawa oś).Ale teraz wygląda na to, że myślisz, że lewa oś zostanie zamieniona na an
int
, a następnie==
znak będzie porównywał dwie liczby całkowite. Tak się nie dzieje. Konwersja zdouble
naint
jest jawna i nie może nastąpić „automatycznie”.Zamiast tego dzieje się odwrotnie. Rs jest zamieniany na
double
, a następnie==
znak staje się testem równości między dwoma podwójnymi. Ta konwersja jest niejawna (automatyczna).Niektórzy uważają, że lepiej jest pisać
if (something == 0.0)
lub
if (something == 0d)
ponieważ wtedy od razu porównujesz dwa podwójne. Jednak to tylko kwestia stylu i czytelności, ponieważ kompilator w każdym przypadku zrobi to samo.
W niektórych przypadkach istotne jest również wprowadzenie „tolerancji”, jak w odpowiedzi Jona Skeeta, ale ta tolerancja też byłaby
double
. Oczywiście mogłoby tak być1.0
, gdybyś chciał, ale nie musi to być [najmniej ściśle dodatnia] liczba całkowita.źródło
Jeśli chcesz po prostu wyłączyć ostrzeżenie, zrób to:
if (something.Equals(0.0))
Oczywiście jest to prawidłowe rozwiązanie tylko wtedy, gdy wiesz, że dryf nie jest problemem. Często robię to, aby sprawdzić, czy mam zamiar podzielić przez zero.
źródło
Nie sądzę, żeby było równe, szczerze. Rozważ własny przykład: coś = 0,9 lub 0,0004. W pierwszym przypadku będzie FALSE, w drugim przypadku będzie TRUE. Mając do czynienia z tego typu typami, zwykle definiuję dla siebie dokładność procentową i porównuję z tą precyzją. Zależy od Twoich potrzeb. coś jak...
if(((int)(something*100)) == 0) { //do something }
Mam nadzieję że to pomoże.
źródło
Oto przykład przedstawiający problem (przygotowany w LinQPadzie - jeśli go nie masz, użyj
Console.Writeline
zamiastDump
metody):void Main() { double x = 0.000001 / 0.1; double y = 0.001 * 0.01; double res = (x-y); res.Dump(); (res == 0).Dump(); }
Oba x i y są teoretycznie takie same i równe: 0,00001, ale z powodu braku „nieskończonej precyzji” te wartości są nieco inne. Niestety na tyle nieznacznie, by wrócić
false
przy porównaniu do 0 w zwykły sposób.źródło