C/NAN-wartosc

Z Wikibooks, biblioteki wolnych podręczników.
< C

Definicje[edytuj]

W pliku math.h są zdefiniowane:

// gcc -lm -Wall l.c
#include <stdio.h>
#include <math.h> // NAN

int main(void)
{
    
#ifdef NAN 
  printf("NAN= %g \n", NAN );
#endif
  return 0;
}

Wynik działania:

NAN= nan 

Opis[edytuj]

NaN (ang. Not A Number - dosłownie: nie-liczba) jest to specjalna wartość w arytmetyce liczb zmiennoprzecinkowych, która reprezentuje wynik nie mający interpretacji liczbowej[1].

Rodzaje NAN[edytuj]

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 (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 przepełnieniem
  • 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ć index 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.

wg implementacji[edytuj]

W Visual C:[2]

  • 1#SNAN oznacza dodatnie sygnalizujące NaN
  • -1#SNAN oznacza ujemne sygnalizujące NaN
  • 1#QNAN oznacza dodatnie ciche NaN
  • -1#QNAN oznacza ujemne ciche NaN
  • 1#IND oznacza pozytywne niezdefiniowane NaN
  • -1#IND oznacza ujemne niezdefiniowane NaN

Operacje generujące NAN[edytuj]

Istnieją trzy rodzaje działań, które mogą zwrócić NaN[3]:

  1. działania w których przynajmniej jeden z argumentów ma wartość NaN;
  2. działania, których wynikiem są symbole nieoznaczone:
    1. dzielenie 0/0 i ±∞/±∞,
    2. mnożenie 0×±∞ i ±∞×0,
    3. dodawanie ∞ + (−∞) i (−∞) + ∞,
    4. odejmowanie ∞ − ∞ i (−∞) − (−∞);
  3. podanie argumentów spoza dziedziny funkcji:
    1. pierwiastek kwadratowy z liczby ujemnej,
    2. logarytm z liczby ujemnej,
    3. funkcje cyklometryczne (arcus sinus lub arcus cosinus) z liczby mniejszej niż −1 lub większej niż +1.

NaN w obliczeniach pojawia się więc z powodu błędu (punkty 2 i 3), bądź propaguje dalej (punkt 1), dlatego ważne jest nie tylko nie dopuszczanie do pojawienia się takiej wartości, ale również możliwe wczesne wykrywanie ewentualnych problemów.

Dla wartości NaN nie są też zdefiniowane relacje, to znaczy dowolne porównanie: większy, mniejszy, równy, itd. NaN z inną wartością (również NaN) będzie zawsze fałszywe[4].

Przykład użycia[edytuj]

#include <stdio.h> /* printf */
#include <math.h> /* isnan */

int main()
{
  double d1;
  double d2;
  double d3;
  int inan; 
  //char *tagp; 
  
  d1 = sqrt (-1) ; // NAN and exceptions defined in IEEE 754 : ‘Invalid Operation’
 /* Checks for nan via x != x are sometimes unreliable (x != x being stripped out by some optimizing compilers that break IEEE compliance, specifically when the -ffast-math switch is enabled). */ 
   if (d1 != d1) printf ("number %f = %g = %e is a NAN \n",d1, d1, d1);
     else printf ("???1 \n");

 d2 = 0.0/0.0; // NAN and exceptions defined in IEEE 754 : ‘Invalid Operation’
 if (isnan(d2)) printf ("number %f = %g = %e is a NAN \n",d2, d2, d2);
     else printf ("???2 \n");

// http://pic.dhe.ibm.com/infocenter/zos/v2r1/index.jsp?topic=%2Fcom.ibm.zos.v2r1.bpxbd00%2Fnan.htm%7Cnan
 // http://www.opensource.apple.com/source/Libm/Libm-2026/Source/nan.c
 d3=nan("");
 if (isnan(d3)) printf ("number %f = %g = %e is a NAN \n",d3, d3, d3);
     else printf ("???3 \n");

 // http://stackoverflow.com/questions/1923837/how-to-use-nan-and-inf-in-c
 inan = 0x7F800001;
 if (isnan(*(float*)&inan)) printf ("number %f = %g = %e is a NAN \n",d2, d2, d2);
     else printf ("???4 \n");
  
 return 0;
}

Zobacz również[edytuj]

Źródła[edytuj]

  1. It's Hard To Compare Floating-Point Numbers March 01, 2013 by Andrew Koenig
  2. What does -1.#IND mean?: A survey of how the Visual C runtime library prints special floating point values by Raymond Chen
  3. David Goldberg: What Every Computer Scientist Should Know About Floating-Point (ang.). [dostęp 2012-04-22].
  4. Checking if a double (or float) is NaN in C++