Dlaczego porównania wartości NaN zachowują się inaczej niż wszystkie inne wartości? Oznacza to, że wszystkie porównania z operatorami ==, <=,> =, <,> gdzie jedna lub obie wartości to NaN, zwraca false, w przeciwieństwie do zachowania wszystkich innych wartości.
Podejrzewam, że upraszcza to w pewien sposób obliczenia numeryczne, ale nie mogłem znaleźć jednoznacznie uzasadnionego powodu, nawet w Notach do wykładu na temat statusu IEEE 754 autorstwa Kahana, które szczegółowo omawiają inne decyzje projektowe.
To odbiegające zachowanie powoduje problemy przy prostym przetwarzaniu danych. Na przykład, podczas sortowania listy rekordów w polu o wartościach rzeczywistych w programie C muszę napisać dodatkowy kod, aby obsługiwać NaN jako element maksymalny, w przeciwnym razie algorytm sortowania mógłby się pomylić.
Edycja: Jak dotąd wszystkie odpowiedzi dowodzą, że porównywanie NaN nie ma sensu.
Zgadzam się, ale to nie znaczy, że poprawna odpowiedź jest fałszywa, raczej byłaby to Not-a-Boolean (NaB), która na szczęście nie istnieje.
Zatem wybór zwracania prawdy lub fałszu dla porównań jest moim zdaniem arbitralny, a dla ogólnego przetwarzania danych byłoby korzystne, gdyby przestrzegał zwykłych praw (zwrotność ==, trikotomia <, ==,>), aby nie było struktur danych które opierają się na tych prawach, stają się zdezorientowane.
Proszę więc o konkretną korzyść z łamania tych praw, a nie tylko filozoficzne rozumowanie.
Edycja 2: Wydaje mi się, że teraz rozumiem, dlaczego zwiększenie NaN byłoby złym pomysłem, co zakłóciłoby obliczanie górnych limitów.
NaN! = NaN może być pożądane, aby uniknąć wykrycia zbieżności w pętli, takiej jak
while (x != oldX) {
oldX = x;
x = better_approximation(x);
}
które jednak lepiej napisać porównując różnicę bezwzględną z niewielkim limitem. Więc IMHO jest to stosunkowo słaby argument za przerwaniem refleksyjności w NaN.
źródło
while (fabs(x - oldX) > threshold)
, wychodząc z pętli, jeśli nastąpi konwergencja lub NaN wejdzie do obliczeń. Wykrywanie NaN i odpowiednie rozwiązanie nastąpiłoby poza pętlą.Odpowiedzi:
Byłem członkiem komitetu IEEE-754, postaram się trochę wyjaśnić.
Po pierwsze, liczby zmiennoprzecinkowe nie są liczbami rzeczywistymi, a arytmetyka zmiennoprzecinkowa nie spełnia aksjomatów arytmetyki rzeczywistej. Trichotomy nie jest jedyną właściwością prawdziwej arytmetyki, która nie obowiązuje dla liczb zmiennoprzecinkowych, ani nawet najważniejszą. Na przykład:
Mógłbym kontynuować. Nie jest możliwe określenie typu arytmetycznego o stałym rozmiarze, który spełnia wszystkie właściwości prawdziwej arytmetyki, które znamy i kochamy. Komitet 754 musi zdecydować o wygięciu lub złamaniu niektórych z nich. Kieruje się to kilkoma prostymi zasadami:
Jeśli chodzi o Twój komentarz „nie oznacza to, że poprawna odpowiedź jest fałszywa”, jest to błąd. Predykat
(y < x)
pyta, czyy
jest mniejszy niżx
. Jeśliy
jest NaN, to nie mniej niż wartość zmiennoprzecinkowąx
, więc odpowiedź jest zawsze fałszywe.Wspomniałem, że trikotomia nie dotyczy wartości zmiennoprzecinkowych. Istnieje jednak podobna właściwość. Klauzula 5.11 ust. 2 normy 754-2008:
Jeśli chodzi o pisanie dodatkowego kodu do obsługi NaNs, zazwyczaj jest możliwe (choć nie zawsze łatwe) takie ustrukturyzowanie kodu, aby NaN przechodziły poprawnie, ale nie zawsze tak jest. Kiedy tak nie jest, może być potrzebny dodatkowy kod, ale jest to niewielka cena za wygodę, jaką zamknięcie algebraiczne wniosło do arytmetyki zmiennoprzecinkowej.
Dodatek: Wielu komentatorów twierdziło, że bardziej przydatne byłoby zachowanie refleksyjności równości i trikotomii na tej podstawie, że przyjęcie NaN! = NaN wydaje się nie zachowywać żadnego znanego aksjomatu. Przyznaję, że mam sympatię do tego punktu widzenia, więc pomyślałem, że wrócę do tej odpowiedzi i przedstawię nieco więcej kontekstu.
Rozumiem z rozmowy z Kahanem, że NaN! = NaN wywodzi się z dwóch pragmatycznych rozważań:
Że
x == y
powinna być równoważnax - y == 0
w miarę możliwości (poza bycie twierdzenie prawdziwej arytmetyki, to sprawia, że realizacja sprzętowa porównania więcej miejsca efektywny, co było niezwykle ważne w czasie średnia został opracowany - Należy jednak pamiętać, że to jest łamane dla x = y = nieskończoność, więc sam w sobie nie jest to świetny powód; mógł być rozsądnie wygięty(x - y == 0) or (x and y are both NaN)
).Co ważniejsze,
isnan( )
w chwili sformalizowania NaN w arytmetyki 8087 nie było predykatu; konieczne było zapewnienie programistom wygodnego i wydajnego sposobu wykrywania wartości NaN, który nie zależał od języków programowania, zapewniając coś takiego,isnan( )
co może zająć wiele lat. Zacytuję własne pismo Kahana na ten temat:Zauważ, że jest to również logika, która wyklucza zwracanie czegoś takiego jak „Nie-Boolean”. Być może ten pragmatyzm został niewłaściwie umieszczony, a standard powinien był wymagać
isnan( )
, ale to sprawiłoby, że NaN byłby prawie niemożliwy do wykorzystania sprawnie i wygodnie przez kilka lat, podczas gdy świat czekał na przyjęcie języka programowania. Nie jestem przekonany, że byłby to rozsądny kompromis.Mówiąc wprost: wynik NaN == NaN nie zmieni się teraz. Lepiej nauczyć się z tym żyć, niż narzekać w Internecie. Jeśli chcesz argumentować, że relacja zamówienia odpowiednia dla kontenerów również powinna istnieć, zalecałbym zalecenie, aby twój ulubiony język programowania implementował
totalOrder
predykat znormalizowany w IEEE-754 (2008). Fakt, że nie mówi jeszcze o słuszności obawy Kahana, która uzasadniała obecny stan rzeczy.źródło
1f/3f == 10000001f/30000002f
? Jeśli wartości zmiennoprzecinkowe są uważane za klasy równoważności,a=b
nie oznacza to „Obliczenia, które przyniosłya
ib
, gdyby zostały wykonane z nieskończoną precyzją, przyniosłyby identyczne wyniki”, ale raczej „To, co wiadomo oa
dopasowaniach, odpowiada temu, co wiadomo ob
„. Jestem ciekawy, czy znasz jakieś przykłady kodu, w którym posiadanie „Nan! = NaN” sprawia, że jest to prostsze niż byłoby w innym przypadku?!(x < 0 || x == 0 || x > 0)
, ale byłoby to wolniejsze i bardziej niezgrabne niżx != x
.NaN może być traktowany jako niezdefiniowany stan / liczba. podobny do pojęcia niezdefiniowanego 0/0 lub sqrt (-3) (w systemie liczb rzeczywistych, w którym żyje zmiennoprzecinkowy).
NaN jest używany jako rodzaj symbolu zastępczego dla tego niezdefiniowanego stanu. Z matematycznego punktu widzenia niezdefiniowany nie jest równy niezdefiniowanemu. Nie można również powiedzieć, że niezdefiniowana wartość jest większa lub mniejsza niż inna niezdefiniowana wartość. Dlatego wszystkie porównania zwracają wartość false.
To zachowanie jest również korzystne w przypadkach, gdy porównujesz sqrt (-3) z sqrt (-2). Oba zwrócą NaN, ale nie są równoważne, mimo że zwracają tę samą wartość. Dlatego pożądane jest zachowanie równości zawsze zwracającej fałsz, gdy mamy do czynienia z NaN.
źródło
!=
Operator zwraca true. PosiadanieNaN==NaN
iNaN!=NaN
oba zwracają wartość false, pozwalając kodowi porównującemu x i y wybrać, co powinno się zdarzyć, gdy oba operandy są NaN, wybierając jeden==
lub!=
.Wrzucić jeszcze jedną analogię. Jeśli podam ci dwa pudełka i powiem ci, że żadne z nich nie zawiera jabłka, czy powiedziałbyś mi, że te pudełka zawierają to samo?
NaN nie zawiera informacji o tym, czym jest coś, tylko czym nie jest. Dlatego tych elementów nigdy nie można z całą pewnością uznać za równe.
źródło
(NaN==Nan)==false
. To, czego nie rozumiem, jest uzasadnieniem(Nan!=Nan)==true
.Z artykułu w Wikipedii na temat NaN następujące praktyki mogą powodować NaN:
Ponieważ nie ma sposobu, aby dowiedzieć się, które z tych operacji stworzyły NaN, nie ma sposobu na porównanie ich, co ma sens.
źródło
Nie znam uzasadnienia projektowego, ale oto fragment standardu IEEE 754-1985:
„Powinna istnieć możliwość porównania liczb zmiennoprzecinkowych we wszystkich obsługiwanych formatach, nawet jeśli formaty operandów różnią się. Porównania są dokładne i nigdy nie są przepełnione ani niedopełnione. Możliwe są cztery wzajemnie wykluczające się zależności: mniejsze niż, równe, większe niż i nieuporządkowane Ostatni przypadek powstaje, gdy co najmniej jednym operandem jest NaN. Każdy NaN porównuje nieuporządkowany ze wszystkim, włączając w to siebie. ”
źródło
Wygląda to tylko dziwnie, ponieważ większość środowisk programistycznych, które pozwalają NaN, nie obsługuje również logiki trójwartościowej. Jeśli wrzucisz do mieszanki logikę o wartości 3, staje się ona spójna:
Nawet .NET nie zapewnia
bool? operator==(double v1, double v2)
operatora, więc nadal masz głupi(NaN == NaN) = false
wynik.źródło
Zgaduję, że NaN (nie liczba) oznacza dokładnie, że: To nie jest liczba, a zatem porównywanie jej nie ma naprawdę sensu.
To jest trochę jak arytmetyka w SQL z
null
operandami: Wszystkie dają wyniknull
.Porównania liczb zmiennoprzecinkowych porównują wartości liczbowe. Dlatego nie można ich używać do wartości nienumerycznych. NaN nie można zatem porównywać w sensie numerycznym.
źródło
FOO
. Aby NaN był równoważny,if (NaN != NaN) foo();
nie powinien zostać wykonanyfoo
, ale tak się dzieje.Zbyt uproszczona odpowiedź jest taka, że NaN nie ma wartości liczbowej, więc nie ma w tym nic do porównania z czymkolwiek innym.
Możesz rozważyć przetestowanie i zamianę swoich NaN na + INF, jeśli chcesz, aby działały jak + INF.
źródło
Chociaż zgadzam się, że porównania NaN z dowolną liczbą rzeczywistą powinny być nieuporządkowane, myślę, że istnieje po prostu powód, aby porównywać NaN z samym sobą. Jak na przykład odkrywa się różnicę między sygnalizowaniem NaN a cichym NaN? Jeśli uważamy sygnały za zbiór wartości boolowskich (tj. Wektor bitowy), można zapytać, czy wektory bitowe są takie same czy różne i odpowiednio uporządkować zestawy. Na przykład przy dekodowaniu maksymalnego tendencyjnego wykładnika, jeśli znacznik zostałby przesunięty w lewo, aby wyrównać najbardziej znaczący bit znaczącego na najbardziej znaczącym bicie formatu binarnego, wartość ujemna byłaby cichym NaN, a każda wartość dodatnia byłaby być sygnalizującym NaN. Zero jest oczywiście zarezerwowane na nieskończoność i porównanie byłoby nieuporządkowane. Wyrównanie MSB pozwoliłoby na bezpośrednie porównanie sygnałów nawet z różnych formatów binarnych. Dwa NaN z tym samym zestawem sygnałów byłyby zatem równoważne i nadają sens równości.
źródło
Dla mnie najprostszym sposobem na wyjaśnienie jest:
Nie można porównywać NaN z czymś innym (nawet samym), ponieważ nie ma ono wartości. Może to również być dowolna wartość (oprócz liczby).
źródło
Ponieważ matematyka jest dziedziną, w której liczby „po prostu istnieją”. W informatyce musisz zainicjować te liczby i zachować ich stan zgodnie z własnymi potrzebami. W tamtych czasach inicjalizacja pamięci działała w sposób, na którym nigdy nie można było polegać. Nigdy nie możesz pozwolić sobie na myślenie o tym „och, to byłoby inicjowane przez 0xCD przez cały czas, moje algo się nie złamie” .
Potrzebujesz więc odpowiedniego nierozmieszalnego rozpuszczalnika, który jest wystarczająco lepki, aby nie dopuścić do wciągnięcia i uszkodzenia algorytmu. Dobre algorytmy obejmujące liczby będą w większości działały z relacjami, a te, jeśli relacje () zostaną pominięte.
To tylko smar, który możesz włożyć do nowej zmiennej podczas tworzenia, zamiast programować losowe piekło z pamięci komputera. A twój algorytm, cokolwiek to jest, nie złamie się.
Następnie, gdy nagle nagle dowiadujesz się, że twój algorytm wytwarza NaN, można go wyczyścić, sprawdzając każdą gałąź pojedynczo. Ponownie zasada „zawsze fałszywa” bardzo w tym pomaga.
źródło
Bardzo krótka odpowiedź:
Ponieważ następujące:
nan / nan = 1
NIE wolno trzymać. W przeciwnym razieinf/inf
byłoby 1.(W związku z tym
nan
nie może być równynan
. Jeśli chodzi o>
lub<
, jeślinan
przestrzegałby jakiejkolwiek relacji zamówienia w zestawie spełniającym właściwość Archimedesa, mielibyśmy ponownienan / nan = 1
na granicy).źródło
inf = inf
iinf / inf = nan
dlatego teżnan = nan
nie zapobiegniemynan / nan = nan
.nan / nan = 1
? W każdym razie ... Twoje rozumowanie ma sens, jeśli inf i nan były takie same jak inne liczby. Tak nie jest. Powód, dla któregoinf/inf
musi byćnan
(lub nieokreślona forma w matematyce) i nie1
jest bardziej subtelny niż prosta manipulacja algebraiczna (patrz twierdzenie De L'Hospitala).