2009-06-24 13 views
13

Czy warto używać wartości zmiennoprzecinkowej IEEE754 NaN (not-a-number) dla wartości nieokreślonych z powodów innych niŜ matematyczne?Czy warto używać wartości bezwzględnej NaN IEEE754 dla wartości, które nie są ustawione?

W naszym przypadku nie zostały jeszcze ustawione, ponieważ wartości nie zostały odebrane z innego urządzenia. Kontekstem jest system wbudowany wykorzystujący wartości REAL32 IEC1131. Edytuj: Język programowania to C, więc najprawdopodobniej użyjemy NAN i isnanf (x), które pochodzą z C99. Chociaż możemy potrzebować dodatkowych wygięć, aby wprowadzić je do naszej warstwy kompatybilności OS.

Domyślnym językiem programowania wydaje się być inicjalizacja zmiennych zmiennoprzecinkowych z dodatnim zerem, którego wewnętrzna reprezentacja jest zerowa. Nie jest to dla nas użyteczne, ponieważ 0 znajduje się w zakresie prawidłowych wartości.

Wygląda na to, że jest to czyste rozwiązanie do korzystania z NaN, ale może jest to więcej kłopotu niż jest warte i powinniśmy wybrać jakąś inną wartość?

+0

Nie znam C, ale w .NET 1.1 zanim pojawiły się typy zerowalne, wiele osób używało takich rzeczy jak minimalna wartość (int.MinValue). Problem polega na tym, że musisz je wszędzie uwzględnić i upewnić się, że nigdy nie użyjesz Wartości minimalnej. Być może coś podobnego istnieje dla C? – RichardOD

+1

Zapytałem o podobne (ale nie to samo) pytanie, może te answeres mogą ci pomóc. http://stackoverflow.com/questions/787828/nan-as-a-special-argument – quinmars

Odpowiedz

10

Właśnie zauważyłem to pytanie.

Jest to jeden z zastosowań NaN, o których myśli komitet IEEE 754 (byłem członkiem komitetu). Reguły propagacji dla NaN w arytmetyce sprawiają, że jest to bardzo atrakcyjne, ponieważ jeśli masz wynik z długiej sekwencji obliczeń, które obejmują niektóre zainicjalizowane dane, nie pomylisz wyniku z prawidłowym wynikiem. Może również śledzić obliczenia, aby znaleźć miejsce, w którym używasz zainicjalizowanych danych o wiele bardziej prosto.

To powiedziawszy, istnieje kilka pułapek, które są poza kontrolą komitetu 754: jak zauważyli inni, nie wszystkie elementy sprzętowe obsługują wartości NaN z prędkością, co może skutkować zagrożeniami wydajności. Na szczęście często nie wykonuje się wielu operacji na zainicjalizowanych danych w ustawieniu krytycznym dla wydajności.

+0

Zaakceptowano, ponieważ w tym przypadku użyliśmy NaN jako niezdefiniowanego, chociaż okazało się, że jest to bardziej kłopotliwe niż oczekiwano. Wynikało to głównie z tego, że brakowało obsługi NaN w naszych narzędziach i systemach lub była ona wadliwa i musieliśmy to obejść. – starblue

3

Użyłem NaNs w podobnych sytuacjach właśnie z tego powodu: standardowa domyślna wartość inicjalizacji 0 jest również poprawną wartością. NaNs działają do tej pory dobrze.

To dobre pytanie, tak przy okazji, dlaczego wartość domyślna inicjalizacji zazwyczaj (na przykład w Java prymitywnych typów) 0 i nie NaN. Czy to nie może być 42 czy jak? Zastanawiam się, jakie jest uzasadnienie zer.

+1

Myślę, że racjonalne uzasadnienie użycia 0 oznacza, że ​​pamięć jest inicjowana z zerową liczbą bajtów niezależnie od typu, na przykład w segmencie BSS C – starblue

+0

Tak, prawdopodobnie to coś takiego.Ale teraz, gdy projektanci języków/kompilatorów podjęli wysiłek inicjowania pamięci, czy nie byłoby prawie tak łatwo zainicjować jakąkolwiek dowolną wartość (inną niż zero)? Zero to po prostu bity między innymi :-) –

+2

@ mad-j: chcesz zainicjować całą pamięć z tym samym wzorem bitowym. Więc nie może to być 42, bo wtedy zwykle musisz zrobić coś innego dla dwóch sąsiednich szortów niż to, co robisz dla int. Pozostawia 0 i -1. Ale 0xffffffff nie ma wartości -1 jako wartości zmiennoprzecinkowej, więc masz tam niespójność. Nie ma w tym zbyt wiele, ale myślę, że 0 jest prawdopodobnie najlepsze. Również niektóre urządzenia mogą wydajnie wykonywać 0 całych bloków pamięci fizycznej na raz, co jest warte. –

0

Jeśli podstawowa potrzeba jest mieć wartość zmiennoprzecinkową, które nie stanowią żadnego numeru, który mógłby być odebrany z urządzenia, i jeśli urządzenie gwarantuje, że nigdy nie powróci NaN, to wydaje się uzasadnione mnie.

Wystarczy pamiętać, że w zależności od środowiska, prawdopodobnie potrzebny jest specjalny sposób wykrywania Nans (nie wystarczy użyć if (x == float.NaN) lub bez względu na to odpowiednik).

+0

Nie wierz w tę odpowiedź. Wszystko, co Jon Skeet musi zrobić, to myśleć o zmiennej, która sama się zdefiniuje. –

+0

Wartość jest zdefiniowana przed Skeet rzeczy o nazwie zmiennej, prawda? – glasnt

4

Koncepcja nieliczby to rozsądny wybór dla „bez wartości” zdaniowa (język programowania D wykorzystuje je do wartości niezainicjowanych, na przykład), ale dlatego, że wszelkie porównania ich udziałem będzie fałszywy, można uzyskać kilka niespodzianek:

  • if (result == DEFAULT_VALUE), nie będzie działać prawidłowo, jeśli DEFAULT_VALUE jest NaN, jak wspomniał Jon.

  • Mogą one również powodować problemy z sprawdzaniem zasięgu, jeśli nie jesteś ostrożny. Rozważmy funkcję:

 
bool isOutsideRange(double x, double minValue, double maxValue) 
{ 
    return x < minValue || x > maxValue; 
} 

Jeśli x jest liczbą, funkcja ta będzie błędnie podają, że x jest między MINVALUE i MAXVALUE.

Jeśli chcesz tylko magiczną wartość dla użytkowników, aby przetestować przed, polecam dodatnią lub ujemną nieskończoność zamiast NaN, ponieważ nie pochodzą z tych samych pułapek. Użyj NaN, gdy chcesz, aby jego własność działała, gdy wszystkie operacje na NaN skutkują NaN: jest to przydatne, gdy nie chcesz polegać na dzwoniących sprawdzających wartość, na przykład.

[Edytuj: początkowo udało mi się wpisać "wszelkie porównania z nimi związane będą prawdziwe" powyżej, co nie jest tym, co miałem na myśli, i jest błędne, wszystkie są fałszywe, poza NaN!= NaN, co jest prawdą]

+0

Który język używa tych reguł porównania? Może D ma. Ale przynajmniej C i C++ nie działają w ten sposób z NaN. Wszystkie porównania zamówień będą fałszywe. x == NaN jest fałszywe dla dowolnego x, w tym NaN. –

+1

Nie, twoja funkcja informuje tylko, że nie jest poza zakresem. Nie jest ani wewnątrz, ani na zewnątrz, co może naiwnie wprowadzać w błąd tych, którzy używają liczb zmiennoprzecinkowych. – starblue

+0

@Igor: Mówimy to samo. isOutsideRange zwróci false, jeśli x jest NaN, co oznacza, że ​​znajduje się wewnątrz zakresu, którego nie ma. – jskinner

1

Mam przeczucie, że jest trochę hacky, ale przynajmniej co drugi numer, który wykonujesz operację z tą wartością NaN, daje NaN jako wynik - gdy zobaczysz NaN w raporcie o błędzie, przynajmniej wiesz, jakiego rodzaju błędu polujesz.

2

Uważaj na NaN ... mogą się rozprzestrzeniać, jeśli nie będziesz ostrożny.

Są to idealnie poprawne wartości dla elementów pływających, ale wszelkie przypisania z nimi związane będą równe NaN, więc będą się rozprzestrzeniać za pomocą kodu. Jest to całkiem dobre narzędzie do debugowania, jeśli je złapiesz, ale może to być również bardzo uciążliwe, jeśli chcesz coś wydać, a gdzieś jest gdzieś na marginesie.

D używa tego jako uzasadnienia dla nadania wartości domyślnej NaN. (Które nie jestem pewien, zgadzam się z.)

+9

Błąd ... Czy to nie tylko kwestia NaNs, które będą propagować? Znacznie lepiej jest mieć NaN jako wynik, co oznacza, że ​​coś jest nie tak, niż mieć niewinnie wyglądającą, ale całkowicie niepoprawną liczbę (która wynikałaby z przypadkowego użycia zerowych liczb początkowych). –

+1

Tak i nie, ponieważ gdy zauważysz NaN tylko patrząc na wynik lub jawnie sprawdzając NaN. Konsekwencją tego jest to, że błędy mogą zostać wykryte znacznie później niż się pojawią. Z drugiej strony, jeśli użyjesz NULL (jeśli to możliwe), otrzymasz dość szybko błąd NPE/segmentacji. Brutalny, ale skuteczny. –

+0

Jeśli wiesz, że NaN są wszędzie, to nie pomoże ci dowiedzieć się, skąd pochodzą. – corsiKa

3

Myślę, że to ogólnie zły pomysł. Należy pamiętać, że większość procesorów traktuje Nan znacznie wolniej niż "zwykły" float. I trudno jest zagwarantować, że nigdy nie będziesz mieć Nan w zwykłych ustawieniach. Moje doświadczenie w obliczeniach numerycznych jest takie, że często przynosi więcej problemów, niż jest warte.

Właściwe rozwiązanie polega na uniknięciu zakodowania "braku wartości" w pływaku, ale sygnalizacji w inny sposób. To nie zawsze jest praktyczne, w zależności od twojego kodu.

0

To brzmi dla mnie jak dobry użytek dla nans. Szkoda, że ​​o tym nie pomyślałem ...

Oczywiście, mają one rozprzestrzeniać się jak wirus, o to właśnie chodzi.

Myślę, że użyłbym nan zamiast jednego z nieskończoności. Przydałoby się użyć nan sygnalizacji i spowodować zdarzenie przy pierwszym użyciu, ale do tego czasu za późno powinno być cicho przy pierwszym użyciu.

0

Używanie NaN jako wartości domyślnej jest uzasadnione.

Należy zauważyć, że niektóre wyrażenia, takie jak (0.0/0.0), zwracają NaN.

Powiązane problemy