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:
- Dodawanie nie jest skojarzone.
- Prawo dystrybucyjne nie obowiązuje.
- Istnieją liczby zmiennoprzecinkowe bez odwrotności.
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:
- Kiedy możemy, dopasowujemy zachowanie prawdziwej arytmetyki.
- Kiedy nie możemy, staramy się, aby naruszenia były jak najbardziej przewidywalne i jak najłatwiejsze do zdiagnozowania.
Jeśli chodzi o Twój komentarz „nie oznacza to, że poprawna odpowiedź jest fałszywa”, jest to błąd. Predykat (y < x)
pyta, czy y
jest mniejszy niż x
. Jeśli y
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:
Możliwe są cztery wzajemnie wykluczające się relacje: mniejszy, równy, większy niż i nieuporządkowany. Ostatni przypadek powstaje, gdy co najmniej jednym operandem jest NaN. Każdy NaN porównuje się nieuporządkowany ze wszystkim, w tym samym sobą.
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żna x - 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:
Gdyby nie było sposobu, aby pozbyć się NaN, byłyby one tak bezużyteczne, jak Nieokreślone w CRAY; gdy tylko zostanie się napotkane, obliczenia najlepiej zatrzymać, a nie kontynuować na czas nieokreślony, aby dojść do nieokreślonego wniosku. Dlatego niektóre operacje na NaN muszą dostarczać wyniki inne niż NaN. Które operacje? … Wyjątkami są predykaty C „x == x” i „x! = X”, które odpowiednio wynoszą 1 i 0 dla każdej liczby nieskończonej lub skończonej x, ale odwrotnie, jeśli x nie jest liczbą (NaN); zapewniają one jedyne proste wyjątkowe rozróżnienie między NaN a liczbami w językach, w których brakuje słowa NaN i predykatu IsNaN (x).
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.
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ą.