NaN
NaN albo nie-liczba (ang. not a number) – wartość numerycznego typu danych oznaczająca niezdefiniowaną lub niereprezentowalną wielkość, zwłaszcza w obliczeniach wykorzystujących liczby zmiennoprzecinkowe. Wartość ta została wprowadzona w 1985 roku przez standard IEEE 754 definiujący binarną postać liczb zmiennoprzecinkowych. Wraz z NaN równolegle wprowadzone zostały także oznaczenia dla nieskończoności.
Zdefiniowane są dwa typy NaN, ciche NaN (qNaN) (ang. quiet NaN) i sygnalizujące NaN (sNaN) (ang. signaling NaN). Ciche NaN są stosowane do propagowania informacji o błędach z wykonania niedozwolonej operacji lub wartości, podczas gdy sygnalizujące NaN mogą wspierać bardziej zaawansowane funkcje, na przykład mieszanie działań liczbowych i obliczeń symbolicznych lub inne rozszerzenia do podstawowej arytmetyki zmiennoprzecinkowej. Zastosowanie NaN obejmuje takie przypadki jak:
- 0/0 nie jest zdefiniowane jako liczba rzeczywista, wobec czego jest reprezentowana przez NaN
- pierwiastek kwadratowy z liczby ujemnej, który jest liczbą urojoną, a więc niereprezentowalną jako rzeczywista liczba zmiennoprzecinkowa, jest określany przez NaN
- wartości NaN są również stosowane do oznaczania brakujących danych w obliczeniach.
Liczby zmiennoprzecinkowe
Osobny artykuł: Liczba zmiennoprzecinkowa.
W obliczeniach zmiennoprzecinkowych NaN nie jest równoważna nieskończoności, mimo że obie te wartości wymagają specyficznej obsługi zarówno od strony cyfrowego zapisu jak i działań zmiennoprzecinkowych. Podobnie nieprawidłowa operacja nie może być utożsamiana z nadmiarem (które może zwrócić nieskończoność) lub niedomiarem (które może zwrócić najmniejszą liczbę normalną, liczbę nieznormalizowaną lub zero).
Według standardu IEEE 754 wartość jest interpretowana jako NaN, jeśli pole cechy ma wszystkie bity ustawione na 1 (podobnie jak nieskończoność), a pole mantysy ma wartość różną od zera (w odróżnieniu od nieskończoności). Taki zapis umożliwia uzyskanie wielu różnych wartości NaN, w zależności od ustawień bitów pola mantysy, jak również od bitu znaku liczby. Najstarszy bit mantysy służy do określenia typu NaN. Jeśli jest on ustawiony na 1 to przyjmuje się, że wartość jest typu ciche NaN, a jeśli ma wartość 0 to jest to sygnalizujące NaN.
Operacje zmiennoprzecinkowe inne niż relacje porównujące zazwyczaj propagują wartości ciche NaN (qNaN). Operacje zmiennoprzecinkowe na sygnalizującym NaN (sNaN) zgłaszają wyjątek wykonania nieprawidłowej operacji. Domyślna akcja wyjątku jest taka sama jak dla działania z qNaN i ustawienia wyniku na qNaN, jeśli działanie zwraca wynik zmiennoprzecinkowy.
Porównanie z wartością NaN zawsze zwraca nieuporządkowany wynik, nawet, jeśli porównuje się NaN z samym sobą. Wyrażenia porównujące mogą być sygnalizujące lub nie, wersja sygnalizująca zgłasza wyjątek wykonania nieprawidłowej operacji w takim porównaniu. Wyrażenia testujące równość lub różność nie są sygnalizujące, więc x = x zwracające fałsz może być wykorzystane do sprawdzenia, czy x zawiera ciche NaN. Wszystkie pozostałe wyrażenia porównujące są sygnalizujące, jeśli argumentem działania jest NaN, standard przewiduje jednak wersje niesygnalizujące również dla tych działań. Wyrażenie isNaN(x) określa czy wartość jest typu NaN i nigdy nie zgłasza wyjątku, nawet jeśli x to sygnalizujące NaN.
Propagacja cichego NaN w obliczeniach arytmetycznych, bez szczegółowych testów podczas etapów pośrednich, pozwala na wykrycie błędu na końcu wykonywania obliczeń. Jednak należy wziąć pod uwagę, że w zależności od języka i funkcji, NaN może zostać zastąpione rzeczywistą wartością w wyrażeniach zwracających stałą wartość, dla wszystkich pozostałych argumentów zmiennoprzecinkowych np. NaN0, które może być zdefiniowane jako 1. W ogólności istnieje potrzeba uzupełnienia warunków sprawdzających poprawność argumentów funkcji po wprowadzeniu wartości typu NaN.
Paragraf 6.2 poprawionego standardu IEEE 754-2008 zawiera definicje dwóch anomalnych funkcji maxnum i minnum, zwracających odpowiednio większą i mniejszą liczbę z pary argumentów. Jeśli jednym z ich argumentów jest NaN a drugim liczba to preferowanym wynikiem jest liczba.
Tworzenie
Istnieją trzy rodzaje działań, które mogą zwrócić NaN[1]:
- działania w których przynajmniej jeden z argumentów ma wartość NaN
- symbole nieoznaczone:
- dzielenie 0/0 i ±∞/±∞
- mnożenie 0×±∞ i ±∞×0
- dodawanie ∞ + (−∞) i (−∞) + ∞
- odejmowanie ∞ − ∞ i (−∞) − (−∞)
- działania na liczbach rzeczywistych zwracające zespolone wyniki:
- pierwiastek kwadratowy z liczby ujemnej
- logarytm z liczby ujemnej
- arcus sinus lub arcus cosinus z liczby mniejszej niż −1 lub większej niż +1
Wartości NaN mogą być jawnie przypisane do zmiennych, typowym zastosowaniem jest reprezentowanie brakujących danych. Zanim wprowadzono standard IEEE, programiści często stosowali wartości specjalne (np. −99999999) aby określić niezdefiniowaną lub brakującą daną, lecz nie gwarantowało to spójności lub poprawności.
Przedstawione powyżej warunki nie gwarantują uzyskania wartości NaN. Jeśli działanie może powodować wystąpienie stanu wyjątku, a zgłaszanie stanów wyjątkowych nie jest zablokowane, to wygenerowany zostanie wyjątek[2]. Jeśli argumentem działania jest ciche NaN, i nie ma innego argumentu o wartości sygnalizujące NaN, to stan wyjątku nie jest generowany, a wynikiem jest ciche NaN. Jawne przypisanie nie generuje wyjątku nawet jeśli jest to sygnalizujące NaN.
Ciche NaN
Ciche NaN (qNaN) nie powoduje generowania wyjątków, gdyż jest propagowane jako wynik większości działań. Jedynym odstępstwem od tej reguły są przypadki, w których NaN nie może być przekazane w niezmienionej formie jako wynik, jak na przykład konwersja formatu lub pewne operacje porównania, które nie są „przygotowane” na argumenty typu NaN.
Sygnalizujące NaN
Sygnalizujące NaN (sNaN) to wyróżnione formy NaN, które przekazane jako argument do większości działań powodują wygenerowanie wyjątku nieprawidłowej operacji, a następnie, jeśli jest to dopuszczalne, są „uciszane” przez przypisanie qNaN, które może być propagowane. Zostały one wprowadzone przez standard IEEE 754, w którym zawarto kilka propozycji na ich zastosowanie:
- początkowe wypełnienie pamięci wartościami sygnalizujących NaN spowoduje wyjątek nieprawidłowej operacji jeśli dane byłyby używane przed ich inicjalizacją,
- używanie sNaN jako wartości zastępczej dla bardziej skomplikowanych obiektów:
- prezentacja liczb z niedomiarem
- prezentacja liczb z nadmiarem
- liczby w formacie o większej precyzji
- liczby zespolone
W przypadku ich wystąpienia, procedura obsługi wyjątku może odkodować informację zawartą w sNaN i zwrócić indeks do obliczonego wyniku. W praktyce takie rozwiązanie napotyka na wiele problemów. Obsługa bitu znaku wartości NaN dla wybranych prostych operacji (np. wartość bezwzględna) jest inna niż dla działań arytmetycznych. Obsługa wyjątków nie jest wymagana przez standard. Istnieją inne rozwiązania takich problemów, które są bardziej przenośne.
Definicja funkcji
Istnieją różne opinie na temat właściwej definicji wyniku funkcji numerycznej, która przyjmuje ciche NaN jako wartość jednego z argumentów. Jednym z punktów widzenia jest to, że NaN powinien być propagowany jako wynik funkcji we wszystkich przypadkach, aby przekazać informację o błędzie. Innym punktem widzenia jest ogólne założenie przyjęte w standardzie IEEE, że jeśli funkcja przyjmuje więcej niż jeden argument, a wynik działania jest ściśle określony przez argumenty różne od NaN i nieskończoności, to obliczony wynik funkcji powinien być zwrócony. Stąd na przykład wartość zwracana przez hypot(±∞, qNaN)
i hypot(qNaN, ±∞)
to +∞.
Problem ten jest szczególnie wyraźnie widoczny w działaniu potęgowania pow(x,y) = x ** y
. Wyrażenia takie jak 00, ∞0 i 1∞ są traktowane jako symbole nieoznaczone, kiedy pojawiają się jako granice, a opinie na temat czy zero do potęgi zerowej powinno być zdefiniowane jako 1 są podzielone.
Jeśli wynik jest traktowany jako niezdefiniowany, jeśli argument jest niezdefiniowany to pow(1,qNaN)
powinien zwracać qNaN. Jednak w typowych implementacjach bibliotek funkcji matematycznych pow(1,y)
zwraca 1 dla dowolnej liczby rzeczywistej y, nawet, jeśli y jest równe nieskończoność lub -nieskończoność. Podobnie wynikiem jest 1 dla wywołania pow(x,0),
nawet jeśli x wynosi 0 lub nieskończoność. W 2008 roku w standardzie IEEE 754 przyjęto założenie, że pow(1,qNaN)
i pow(qNaN,0)
powinno w obu przypadkach zwracać 1, gdyż owe funkcje zwracają 1 dla wszystkich pozostałych argumentów.
Aby zapewnić istnienie funkcji potęgowania ze ściśle określonymi regułami zgodnymi z NaN, w 2008 roku do standardu dodano dwie dodatkowe definicje: pown(x,n),
gdzie wykładnik musi być liczbą całkowitą, i powr(x,y)
, która zwraca NaN, jeśli jeden z jej argumentów jest NaN lub wyrażenie potęgowania sprowadza się do symbolu nieoznaczonego.
NaN w arytmetyce całkowitoliczbowej
Większość typów całkowitoliczbowych o stałym rozmiarze nie ma żadnej metody prezentacji nieprawidłowych danych.
Moduł BigInt w Perlu stosuje „NaN” na wynik z tekstów nie zawierających prawidłowych liczb całkowitych.
>perl -mMath::BigInt -e "print Math::BigInt->new('foo')" NaN
Wyświetlanie
Różne systemy operacyjne i języki programowania mają różne reprezentacje NaN w postaci tekstowej.
nan NaN NaN% NAN NaNQ NaNS qNaN sNaN 1.#SNAN 1.#QNAN -1.#IND[a]
Ponieważ, w praktyce, NaN w zapisie cyfrowym ma bit znaku i dodatkowe „informacje diagnostyczne” (czasami zwane ładunkiem), to często można spotkać się z ich uwzględnieniem w postaci tekstowej, np.:
-NaN NaN12345 -sNaN12300 -NaN(s1234)
(istnieją też inne warianty)
Zapis cyfrowy
W standardzie IEEE 754, definiującym format liczb zmiennoprzecinkowych, NaN jest rozpoznawany przez unikatowy wzór bitów zarezerwowany dla wartości NaN. Bit znaku jest bez znaczenia. Binarna postać NaN jest oznaczana przez ustawienie wszystkich bitów pola cechy na jeden (jak w nieskończoności), a wartość pola mantysy jest różna od zera (w odróżnieniu od nieskończoności). Pierwotna specyfikacja standardu IEEE 754 z 1985 roku tylko opisywała binarny format zapisu liczb zmiennoprzecinkowych, a nie specyfikowała jak należy oznaczyć stan cichy i sygnalizowany. W praktyce najbardziej znaczący bit pola mantysy określa czy NaN jest ciche, czy sygnalizowane. W efekcie uzyskano dwie, przeciwne znaczeniowo, implementacje.
- Większość procesorów (włączając w nie rodziny Intel/AMD x86-32/x86-64, Motorola 68000, AIM PowerPC, Architektura ARM, Sun SPARC) ustawia najbardziej znaczący bit na wartość niezerową jeśli NaN jest ciche, a na wartość zero jeśli NaN jest sygnalizujące. Stąd, na tych procesorach, bit ten można określić jako flaga „czy_ciche”.
- W NaN utworzonych na procesorach PA-RISC i MIPS, najbardziej znaczący bit ma wartość zero jeśli NaN jest ciche, a nie-zero jeśli NaN jest sygnalizujące. Stąd, na tych procesorach, bit ten można określić jako flaga „czy_sygnalizujące”.
W roku 2008 poprawka do standardu IEEE 754 wprowadziła zalecenia na kodowanie bitu zawierającego informację o typie NaN.
- Dla formatu binarnego, standard określa interpretację zgodną z flagą „czy_ciche”, tj. bit określający rodzaj NaN jest ustawiony na wartość niezerową jeśli NaN jest ciche, a wartość zero jeśli NaN jest sygnalizujące. Bit ten powinien być najbardziej znaczącym bitem w polu mantysy.
- Dla formatu dziesiętnego, kodowanego binarnie lub dziesiętnie, NaN jest zdefiniowany przez ustawienie na jeden najstarszych pięciu bitów pola skojarzonego po bicie znaku. Szósty bit tego pola jest flagą „czy_ciche”. Standard określa interpretację zgodną z flagą „czy_sygnalizujące”, tj. bit ma wartość zero jeśli NaN jest ciche, i nie-zero jeśli NaN jest sygnalizujące.
Stan/wartość pozostałych bitów (tj. innych niż te użyte do określania, że wartość jest NaN i jej typu) nie jest zdefiniowana przez standard, z wyjątkiem ograniczenia, że nie mogą być wszystkie jednocześnie ustawione na zero. Wartość ta jest zwana ładunkiem NaN i zaleca się przekazywać wartość pierwszego argumentu qNaN przez wszystkie działania zwracające na wyjściu qNaN celem wsparcia debugowania.
Zobacz też
Uwagi
- ↑ Nieokreślone (ang. indefinite)[3] – specjalny przypadek cichego NaN. Standard IEEE 754 wymaga aby dla wybranych przypadków zgłosić ciche NaN, lecz nie definiuje dokładnie jaka jest jego wartość. W rezultacie różni producenci procesorów zmiennoprzecinkowych wybierają różną wartość. Oddzielny symbol w takim przypadku pozwala go wyróżnić spośród pozostałych reprezentacji cichego NaN.
Przypisy
- ↑ David Goldberg: What Every Computer Scientist Should Know About Floating-Point. [dostęp 2012-04-22]. (ang.).
- ↑ Intel 64 and IA-32 Architectures Software Developer’s Manual Volume 1: Basic Architecture. April 2008. s. 118–125, 266–267, 334–335. (ang.).
- ↑ Raymond Chen: What does -1.#IND mean?: A survey of how the Visual C runtime library prints special floating point values. The Old New Thing, 2013-02-21. [dostęp 2013-02-22]. (ang.).