2009-10-30 14 views

Odpowiedz

44

Naprawdę nie można porównać wartości zmiennoprzecinkowych i całkowych w sposób naiwny; w szczególności, ponieważ jest klasyczny: floating pointrepresentation challenges. Co ty może zrobić to odjąć od siebie i sprawdzić, czy różnica między nimi jest mniejsza niż pewna precyzja Ci zależy, tak jak poniżej:

int iValue = 0; 
double dValue = 0.0; 

var diff = Math.Abs(dvalue - iValue); 
if(diff < 0.0000001) // need some min threshold to compare floating points 
    return true; // items equal 

naprawdę trzeba określić dla siebie, co equality oznacza dla Ciebie . Na przykład, możesz chcieć, aby wartość zmiennoprzecinkowa zaokrąglała w kierunku najbliższej liczby całkowitej, tak aby 3.999999981 była "równa" do 4. Możesz też skrócić wartość, aby była efektywna 3. Wszystko zależy od tego, co Ty próbuję to osiągnąć.

EDIT: Uwaga że wybrałem 0,0000001 jako wartość progową przykład ... musisz sam zdecydować, co jest wystarczające dla precyzyjnego porównania. Po prostu zdaj sobie sprawę, że musisz znajdować się w normalnych granicach reprezentacyjnych double, które według mnie są zdefiniowane jako Double.Espilon.

+0

To jest dobre rozwiązanie problemu, o którym wspomniałem. Dla większej dokładności (tylko gdy jest to konieczne), używaj tylko liczb całkowitych, jak zasugerowałem w mojej odpowiedzi. –

+0

Czy lepiej porównać ze stałą systemową epsilon zdefiniowaną przez system (zobacz tutaj http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx) zamiast wartości zakodowanej na stałe, np. 0.0000001? NB Nigdy nie kodowałem C# (tylko C++), ale zakładam, że ta sama zasada obowiązuje – pxb

+1

Wspominam o tym w edytorze mojej odpowiedzi. Epsilon może, ale nie musi być dobrym wyborem, w zależności od tego, jaką precyzję OP obchodzi w swoim kodzie. – LBushkin

1

To naprawdę zależy od tego, co uważasz za "równe". Jeśli chcesz, aby Twoje porównanie wrócić prawdziwe wtedy i tylko wtedy, gdy dwukrotnie dokładnie pasuje do wartości całkowitej (czyli nie ma składnik ułamkowej), należy oddać int do podwójnego zrobić porównania:

bool isEqual = (double)iValue == dValue; 

jeśli coś 1.1 byłby uważany za równy 1, możesz rzucić podwójny do int (jeśli chcesz całkowicie zignorować element ułamkowy) lub zaokrąglić podwójny, jeśli chcesz powiedzieć 1,9 na równe 2.

+0

Ostrożnie z zaokrągleniem. Wiele osób wydaje się zaskoczonych, gdy 2,5 rundy na 2. – Joey

+0

@Joey To dość trywialne, aby napisać własny kod zaokrąglania, aby zachować pożądane zachowanie, jeśli te trzy wbudowane nie spełniają twoich potrzeb. – Casey

3

To niezmiernie zły pomysł porównać liczby całkowite i liczby zmiennoprzecinkowe dla równości w dowolnym języku. Działa to w bardzo prostych przypadkach, ale po wykonaniu jakiejkolwiek matematyki prawdopodobieństwo, że program robi to, co chcesz, dramatycznie się zmniejsza.

Ma to związek ze sposobem, w jaki liczby zmiennoprzecinkowe są przechowywane w cyfrowym systemie binarnym.

Jeśli jesteś bardzo pewny, że chcesz tego użyć, utwórz klasę, aby utworzyć własny numer z ułamkami. używaj jednej int do zachowania liczby całkowitej, a innej int do utrzymania frakcji.

0

Obecnie tylko o tylko raz jeden powinno być porównanie wartości typów double i albo integer lub long ścisłej równości jest, gdy z jakiegoś powodu jeden jest zablokowany przechowywania lub przechodząc integralne ilości jako wartości zmiennoprzecinkowych i późniejszych potrzeb aby je przekonwertować. Taka konwersja może w większości przypadków być najłatwiejsza poprzez odlanie typu całkowego na double, a następnie porównanie wyniku tego rzutu. Zauważ, że konwersja z long na double może być niedokładna, jeśli liczba jest poza zakresem ± 2 . Niemniej jednak, w czasach przed udostępnieniem 64-bitowego long, double był podręcznym typem pamięci dla liczb całkowitych, które były zbyt duże dla 32-bitowego int, ale wystarczająco małe, aby obsłużyć je double.

Zauważ, że konwertowania long do double a następnie robi porównanie przyniesie to rezultat „równe”, jeżeli wartość nominalna double nie dokładnie dopasować wartość long, ale reprezentuje najbliższy możliwy double do tej wartości. Takie zachowanie ma sens, jeśli rozpoznaje się, że zmiennoprzecinkowe typy w rzeczywistości nie reprezentują pojedynczej dokładnej wartości, a raczej zakres wartości.

2
double val1 = 0; 
double val2 = 0.0; 
if((val1 - Double.Epsilon) < 0) 
{ 
    // Put your code here 
} 

     OR 

if((val2 - Double.Epsilon) < 0) 
{ 
    // Put your code here 
} 

gdzie Double.Epsilon jest najniższą możliwą wartością dla Double.