O liczbie NaN
O liczbach w programowaniu nie można nie rozmawiać. W szczególności o tak ważnej liczbie jaką jest NaN. W ramach tego artykułu postaram się przyjrzeć matematyce, liczbom, koncepcji NaN i jej działaniu w świecie kodu.
Z tego artykułu dowiesz się:
Co to są liczby zmiennoprzecinkowe?
Co to jest i jak działa konkatenacja?
Co zawiera standard IEEE-754 i kto go przygotował?
Dlaczego NaN jest liczbą?
Matematyka od zawsze leżała u podstaw programowania. Co prawda, programowanie w językach wysokopoziomowych często nie wymaga od nas znajomości skomplikowanych algorytmów czy wzorów skróconego mnożenia, nie musimy znać całek i rachunku różniczkowego - wystarczy, że umiemy układać kod w logiczną całość, którą maszyna będzie w stanie zrozumieć. Ot i cała zagadka.
Ale… matematyka od zawsze leżała u podstaw programowania. Jakby nie patrzeć to właśnie ona pozwala na komunikację pomiędzy nami a komputerem czy też pomiędzy różnymi urządzeniami elektronicznymi. Wszak pod spodem to tylko poukładane w odpowiedniej kolejności 0 i 1. Dochodzimy zatem do prostego wniosku - komputery komunikują się za pomocą liczb.
Liczby, liczby, liczby
Początki tworzenia systemów liczbowych sięgają starożytności. Już w Babilonii stosowano cyfry o wartościach od 1 do 10, określając ich wartość na podstawie kolejności cyfr w szeregu. Na pewno znasz też nazwiska takich filozofów jak Pitagoras, Euklides, Archimedes - to im przypisujemy pierwsze prace nad liczbami.
Liczby są wokół nas. Wyrażamy w nich czas, numer peronu, z którego odjeżdża nasz pociąg, pojemność silnika naszego pojazdu, odległość od biura czy ilość pamięci RAM. Nie zdając sobie z tego sprawy akceptujemy liczby, a zatem matematykę w całej jej okazałości w naszym życiu. Matt Parker w swojej książce Pi razy oko (ang. Humble Pi) stawia tezę, że jako ludzie mamy problem z tą dziedziną. Nie radzimy sobie zbyt dobrze z szacowaniem wielkich liczb, prostymi obliczeniami, ułamkami, liczbami ujemnymi i wieloma innymi matematycznymi konceptami - dopóki nie przejdziemy odpowiedniego szkolenia.
O ile życie byłoby prostsze, gdybyśmy korzystali tylko z liczb całkowitych, a najlepiej naturalnych. Najlepiej w przedziale od 1 do 10, tak aby wystarczyło nam palców na dłoniach do wykonywania obliczeń. Ciekawa myśl, prawda?
Niestety, nie da się. Gdyby nie te skomplikowane koncepty, tak nienaturalne dla naszych umysłów, nasza cywilizacja nie rozwijała by się tak dynamicznie. Nie dotarlibyśmy do punktu, w którym aktualnie jesteśmy.
Liczby zmiennoprzecinkowe
O liczbach całkowitych nie trzeba dużo mówić. Zastosowań dla nich jest wiele. Sytuacja się komplikuje, kiedy chcemy wyrazić, że potrzebujemy czegoś z przedziału pomiędzy jedną a drugą liczbą całkowitą. Już widzę w wyobraźni minę Pana Mariusza z osiedlowej piekarni, kiedy proszę o kawałek chleba (nie 0, ale też nie 1 cały), na oko mniej więcej 15 cm. Wyjaśnienie mu ile potrzebuję chleba byłoby drogą przez mękę dla nas obu. A gdyby tak powiedzieć - poproszę pół chleba. Przecież naturalnie rozumiemy ile to jest połowa.
Pozostaje nam tylko zapis tej wartości. Matematyka pozwala nam używać ułamków, czyli części całości - w powyższym przykładnie byłoby to 1/2. Pomimo, że klasycznie zapisywane ułamki są cudowne w swej prostocie, nie dają rady w zetknięciu w rzeczywistością. Świat bowiem potrzebuje usystematyzowania. Postanowiono zatem, że jeśli na co dzień korzystamy z systemu dziesiętnego, to ułamki również będziemy przedstawiać w formie części dziesiętnych. I tak oto 1/2 stała się 0,5.
Liczby zapisane z przecinkiem (kropką w języku angielskim) to liczby zmiennoprzecinkowe.
Operacje na liczbach
Skoro mamy już w swoim arsenale liczby to dobrze byłoby móc z nimi coś zrobić. Dodawać, odejmować, dzielić, mnożyć, obliczać resztę z dzielenia, potęgować itd. W programowaniu wykonujemy wszystkie te działania matematyczne. Nie musimy co prawda weryfikować wyniku tych obliczeń, ale jesteśmy poniekąd zobligowani do rozumienia w jakim celu wykonujemy daną operację i jakiego mniej więcej rozwiązania możemy się spodziewać. Niefrasobliwość w tym aspekcie może prowadzić do wielu przykrych sytuacji, niebezpiecznych dla naszego zdrowia i portfela. Według szacunków w roku 2012 firma JPMorgan Chase straciła około 6 miliardów dolarów. Z opublikowanych wyjaśnień wynika, że strata nastąpiła w skutek błędu w szacowaniu ryzyka, a dokładniej błąd powstał we wzorze odpowiedzialnym za dane wyliczenia. Do wzoru podstawiono sumę wartości, zamiast ich średnią. Wynik przekłamał prawdziwy obraz ryzyka inwestycyjnego. Chyba nie trzeba dodawać więcej.
Ponownie, o ile prościej byłoby gdybyśmy wykonywali te operacje tylko na liczbach. W językach programowania mamy dostępnych wiele różnych typów danych, nie tylko liczby - w niektórych bardzo łatwo dodać do siebie dwa różne typy, np. ciąg znaków i liczbę.
Python nie zezwoli nam na wykonanie takiej operacji. Przed wykonaniem tego działania sprawdzi typy zmiennych i zablokuje daną operację, zwracając błąd.
Inaczej ma się sytuacja z językami dynamicznie typowanymi, jak Javascript.
Mechanizm Javascriptu sprawdza typy dodawanych danych i jeśli przynajmniej jeden jest stringiem, wykonuje operację konkatenacji a nie dodawania (ten sam operator +
odpowiada za obie te operacje). Konkatenacja to operacja łączenia ze sobą stringów. W trakcie jej wykonywania Javascript dokona konwersji typów łączonych wartości do stringów. I tak oto powstaje mały problem.
Zagubiony typ
Zdarzyć się może, że pominęliśmy jakiś typ i zamiast liczby do operacji dodawania przekazaliśmy string. Umknęło nam to i nie wychwyciliśmy tego na czas. Kod trafił na środowisko produkcyjne, a tam użytkownikowi wyświetla się komunikat: „Na Twoim koncie pozostało NaN złotych.” . Coś zdecydowanie poszło nie tak. Co to jest to NaN?
Warto przeczytać w tym temacie
NaN
Instytut Inżynierów Elektryków i Elektroników, czyli IEEE, to organizacja, która od 1963 roku ustala standardy dla urządzeń elektronicznych i formatów komputerowych. Jednym z wydanych przez nią standardów jest standard IEEE-754. Mówi on przede wszystkim o reprezentacji binarnej i operacjach na liczbach zmiennoprzecinkowych. Standard dość powszechny, bo wykorzystany w procesorach i oprogramowaniu obliczeniowym. W ramach zakresu tego standardu zdefiniowano pojęcie NaN - Nie Liczby (ang. Not-A-Number). W ramach IEEE-754 opisano dwie specjalne wartości określone jako NaN - ciche QNaN (ang. Quiet NaN) oraz głośne SNaN (ang. Signaling NaN). Na cichych NaN można wykonywać operacje arytmetyczne, ponieważ najczęściej oznaczają wartość niezdefiniowaną i nie powodują błędu. Głośne NaN najczęściej prowadzą do przerwania operacji i wyrzucenia wyjątku. SNaN jest często wykorzystywane podczas operacji inicjowania zmiennych - jeśli nie zainicjujemy zmiennej (tj. nie podamy jej wartości) - próba wykorzystania takiej zmiennej w działaniu matematycznym doprowadzi do zwrócenia błędu i przerwania operacji.
Liczba czy nie?
NaN z definicji jest specjalną wartością, która nie reprezentuje liczb. Aczkolwiek w wielu językach programowania wartość NaN jest typu numerycznego.
Dzieje się tak dlatego, że wartość NaN otrzymamy, gdy wykonamy operację matematyczną, a jej wynik nie będzie mógł być wyrażony za pomocą liczb. Dlatego jeśli spróbujemy porównać NaN z innym NaN, zawsze otrzymamy wartość false
. Każde NaN zawsze będzie inną wartością.
Nie jest to specyfika Javascriptu jako języka. Warto podkreślić, że jest to ogólny koncept przyjęty w świecie programowania.
Podsumowanie
Nie chciałbym poruszać w tym artykule sposobów porównywania wartości NaN w języku Javascript. Pewnie kiedyś napiszę na ten temat oddzielny wpis. Warto natomiast zapamiętać, że NaN i jej istnienie nie jest specyficznym działaniem Javascriptu. Jest to ogólny koncept, który został zarysowany w ramach standardu IEEE-754.
Nie potrzebujemy mieć zaawansowanej wiedzy matematycznej, żeby programować, ale od matematyki w programowaniu nie uciekniemy. Liczby nas otaczają w prawdziwym świecie, nie powinno nas zatem dziwić, że są dostępne również w swoim naturalnym środowisku - komputerowym.
Komentarze (0)
Jeszcze nikt nic nie napisał, ale to znaczy że... możesz być pierwszy/pierwsza.