Studia: Podstawy Informatyki — systemy liczbowe
Nadszedł w końcu ten czas, kiedy kończy się szkoła średnia i rozpoczyna się nowy okres życia – studia. Wydawałoby się, że w czasie studiów na aktywności takie jak blog będzie o wiele mniej czasu. Ale jest (przynajmniej teraz i w moim wypadku) dokładnie na odwrót.. W związku z tym pomyślałem, że niezłym pomysłem byłoby opisywanie jak największej ilości zagadnień pojawiających się na studiach na łamach tego bloga. W tym miejscu będę zajmował się jedynie działami nieco ściślej związanymi z IT. Reszta przedmiotów, takich jak fizyka, jest umieszczona tutaj.
Od czego zaczniemy? Jeśli w tej chwili kojarzysz cokolwiek z materii IT, już sam tytuł powinien ci co nieco wyjaśnić sprawę. Otóż, będziemy zajmowali się systemami liczbowymi, ich przekształceniami oraz sposobem ich zapisu w pamięci komputera.
Nie ma co zwlekać. Do dzieła!
System dwójkowy
System binarny, inaczej zwany dwójkowym, jest podstawą działania całej współczesnej techniki cyfrowej. Praktycznie każdy komputer na świecie operuje właśnie na takim, a nie innym systemie liczbowym. Podejmowano próby zaimplementowania innych, ale spaliły one na panewce lub – w najlepszym przypadku – znalazły swoją niszę w której jakoś do tej pory egzystują. Lecz inne systemy liczbowe nigdy nie zastąpiły systemu dwójkowego całkowicie. Dlaczego? Przede wszystkim ze względu na prostotę „przetłumaczenia” systemu binarnego na zachowanie układów elektronicznych. Np.: 1 – włączone, 0 – wyłączone.
Tylko nie myśl od razu, że mamy tylko jeden rodzaj systemu dwójkowego – o nie! To by było stanowczo za proste. Wyróżniamy dwa rodzaje systemów binarnych:
- Naturalny kod binarny (NKB) – nie zapiszemy w nim liczb ujemnych, ale jest o wiele prostszy.
- System uzupełnień do dwóch (U2) – nieco bardziej skomplikowany w przeliczaniu. Możemy w nim zapisać liczby ujemne
Zanim przejdziemy do analizy działania obydwu systemów liczbowych, zastanówmy się, czy nie można byłoby używać tylko systemu U2? Nie, gdyż w wielu wypadkach nie potrzebujemy liczb ujemnych, a U2 niejako "o połowę" zmniejsza nam maksymalną wartość, jaką możemy zapisać w danym obszarze pamięci. Są jeszcze inne cechy NKB, ale na tę chwilę są one nieistotne.
Zaczniemy od prostszego naturalnego kodu binarnego (NKB). Ale najpierw poznajmy pewną ogólną zasadę, rządzącą wszystkimi systemami liczbowymi. Dzięki niej będziemy mogli utworzyć system o dowolnej podstawie – dziewiątkowy, jedenastkowy, dziewiętnastkowy itd.. Spójrz na poniższą liczbę i to, co znajduje się po znaku równości. Z pewnością kojarzysz taki zapis jeszcze z podstawówki:
Zapiszmy teraz dziesiątki w formie potęg.
Jak widzisz, każdą liczbę w systemie dziesiętnym da się rozpisać jako sumę cyfr pomnożonych przez odpowiednie potęgi liczby dziesięć. Potęgi odpowiadają za pozycję danej cyfry w omawianej liczbie. Cyfra 3 zostanie pomnożona przez 10^2, a więc w omawianej trzycyfrowej liczbie znajdzie się na jej samym początku. 4, która będzie pomnożona przez 1 będzie najmniej znaczącą cyfrą i znajdzie się na samym końcu omawianej liczby.
Jeśli uważnie się przyjrzałeś, zauważyłeś pewnie, że przy liczbie 394 w indeksie dolnym napisałem słówko DEC. Jak pewnie się domyślasz, oznacza ono, że liczba zapisana jest w systemie liczbowym. Żelazna zasada – gdy operujesz różnych systemach liczbowych, nigdy nie zapominaj o indeksie. Dlaczego? Gdyż, jeśli nie zapiszesz indeksu, to tak naprawdę dana cyfra nic nie oznacza. Bo np.: 394 bez indeksu istnieje zarówno w systemie dziesiętnym, jak i każdym innym o podstawie większej niż 10. Bez dokładnego określenia systemu, w którym dana liczba jest zapisana, tak naprawdę nie będziemy znali wartości tej liczby. Będziemy mogli tylko zgadywać, co nie zawsze i nie dla każdego skończy się pozytywnie.
Ale jak powyższe zasady mają się do systemu binarnego? Zobaczmy.
NKB – najprostszy możliwy system binarny
NKB->DEC Skrót NKB, oznacza Naturalny Kod Binarny. Jest to najprostszy system, jaki poznamy. Pewnie potrafisz sobie wyobrazić liczbę w systemie binarnym. Może to być np.: 10110011. Ale co ona oznacza? Jak ją przeliczyć na system rozumiany przez nas – ludzi? Wystarczy wykorzystać ogólną zasadę, o której wspomniałem kilka linijek wcześniej. No to zaczynamy.
Uff, rozpisanie poszczególnych „wag” trochę miejsca nam zajęło. Teraz zsumujmy to wszystko
Otrzymaliśmy wynik. I jednocześnie nauczyliśmy się przeliczać naturalny kod binarny na system dziesiętny. Klawo, nie? Ale to dopiero początek naszych wysiłków. Teraz pomyślmy, jak można przekształcić liczbę zapisaną w systemie dziesiętnym na dwójkową. DEC->NKB[ W poprzednim przykładzie przekształciliśmy liczbę binarną na jej dziesiętny odpowiednik. Teraz, wykorzystując tę samą liczbę (przynajmniej wiemy, jaka jest poprawna odpowiedź xD) przeprowadzimy konwersję w drugą stronę. Ponownie skorzystamy z zapomnianych technik, których używaliśmy jeszcze w podstawówce. Spójrz na poniższy przykład:
Co zrobiliśmy? Po prostu dzieliliśmy otrzymaną do przekształcenia liczbę przez 2(podstawę systemu) zapisując kolejne reszty. Lecz zwróciłeś pewnie uwagę, że przy pierwszym i ostatnim wierszu znajdują się tajemniczo wyglądające nawiasy – najmniej znaczący bit/najbardziej znaczący bit. Co one oznaczają? Uproszczając, najbardziej znaczący bit to ten, który stoi na samym początku liczby, czyli bit pierwszy. Natomiast najmniej znaczący to ten, który zamyka bitowy marsz. Musimy mieć tą wiedzę, aby poprawnie zinterpretować otrzymany wynik. Jak pewnie zauważyłeś, zapisując reszty z dzielenia od najbardziej znaczącego bitu do najmniej znaczącego, otrzymaliśmy dokładnie tę samą cyfrę, którą mieliśmy na początku.
U2 – coś do zapisu liczb ujemnych
Jak doskonale się domyślasz, naturalny kod binarny nie jest idealnym narzędziem. Nie pozwala nam m.in. na zapis liczb ujemnych, które są znane i używane od bardzo dawna. Aby temu zaradzić, opracowano specjalną odmianę kodu binarnego, zwaną systemem uzupełnień do dwóch (w skrócie – U2). Zapamiętaj jedną ważną rzecz – U2 od NKB odróżnia tylko interpretacja poszczególnych bitów. W U2 najbardziej znaczący bit ma ujemną wagę. Spójrz na poniższy przykład, w którym ten sam ciąg bitów jest interpretowany najpierw jako NKB, a potem jako U2.
Jak widzisz, nawet w najprostszej informatyce nie zawsze istnieje jednoznaczność. Jeśli nie będziemy wiedzieli, w jakim rodzaju systemu dwójkowego została zapisana dana liczba, nie odczytamy jej tak, jak powinniśmy. W powyższym przykładzie liczbę 11110101 można odczytywać dwojako – jako 245 (jeśli to kod NKB) lub -11 (jeśli to U2). Prawda, że istnieje dosyć spora różnica między tymi wynikami?
Poznajmy teraz metody przekształcania liczb do systemu U2
NKB->U2 Przekształcenie liczby zapisanej w naturalnym kodzie binarnym do u2. Jest stosunkowo proste. Co robimy? Jak wiesz, w NKB nie da się zapisać liczby ujemnej. Więc najbardziej znaczący bit musi być wyzerowany. Nie możemy wyzerować żadnego z istniejących bitów, bo wtedy zmieniłaby się wartość liczby. Jest na to sposób – wystarczy dopisać 0 przed całą liczbą. Spójrz niżej:
Możesz przekształcić otrzymaną liczbę na system dziesiętny, jeśli mi nie wierzysz. Prawda, że to jest proste? Ale już za chwilę przejdziemy do trudniejszego zagadnienia, a mianowicie: DEC->U2 W przypadku, gdy liczba dziesiętna jest dodatnia, nie ma tutaj wielkiej filozofii. Wykonujemy zamiany DEC‑>NKB. Otrzymana liczba jest de facto liczbą w systemie U2. Większym problemem jest zamiana ujemnej liczby dziesiętnej na system U2. Ale od czego jest ten blog, jak nie od tego, aby w klarowny sposób przedstawić ten proces? Zaczynamy.
Na początku warto wspomnieć, że istnieją dwa sposoby na dokonanie tej czynności. Rozpocznę od tego bardziej znanego, ale jednocześnie trudniejszego.
Niech naszą liczbą, którą będziemy chcieli przekształcić, będzie -93. Pierwszym krokiem jest przekształcenie tej liczby do systemu NKB. Ale jak to zrobić, gdy mamy minus? Odpowiedź jest banalna – zastosujemy wartość bezwzględną.
Teraz przekształćmy otrzymaną liczbę do systemu NKB:
A więc:
Teraz dopisujemy na samym początku zero.
Zapytasz pewnie: „Mieliśmy przecież zapisać liczbę ujemną!”. A na początku mamy zero … Ale to jeszcze nie koniec naszej pracy. Teraz musimy wykonać negację każdego bitu, czyli, mówiąc najprościej, tam gdzie jest jedynka wstawić zero, a tam gdzie zero – jedynkę. u
Ostatnim krokiem jest dodanie binarnej jedynki do otrzymanej liczby. Dodawanie binarne omówimy na końcu tego artykułu. Tutaj podam tylko gotowy wynik.
Jak pewnie zauważyłeś po indeksie, właśnie ukończyliśmy ostatni etap konwersji ujemnej liczby dziesiętnej do systemu U2. Teraz możemy jeszcze wykonać sprawdzenie:
Jak widzisz, w tej metodzie dużo jest miejsc, w których można się pomylić. A to jakieś negacje, a to dodawania jedynek… Drugi sposób jest o wiele przyjemniejszy. Aby pominąć ten cały trud przeliczania liczby dziesiętnej na binarną. Przyjmijmy, że chcemy zamienić na system U2 liczby -184 i -148. Zaczynamy, podobnie jak wcześniej, od wyciągnięcia wartości bezwzględnej z liczby.
Teraz zapisujemy liczbę w systemie NKB.
Dopisujemy na początku zero, gdyż (póki co) te liczby są dodatnie
I teraz uwaga: Negujemy wszystkie bity, oprócz najmłodszych zer i pierwszej napotkanej najmłodszej jedynki. Brzmi enigmatycznie, prawda? Dlatego najlepiej, jeśli przeanalizujesz poniższy przykład:
Na czerwono zaznaczyłem część, która się nie zmienia – a więc wszystkie najmłodsze zera i pierwsza najmłodsza jedynka. Całą resztę, jak widzisz wyżej – negujemy. Drugi przykład: (148)
Podobnie, jak ostatnio, niezmienione pozostały najmłodsze zera i pierwsza najmłodsza jedynka. A więc wynik to:
Dodawanie i odejmowanie liczb binarnych
Dodawanie i odejmowanie liczb to dwa podstawowe działania, jakie będziesz wykonywał. Jest ono bardzo proste. Zasady działania są identyczne jak w przypadku dodawania/odejmowania liczb dziesiętnych. Załóżmy, że chcemy dodać liczbę 11001010 i 1111 Najpierw zapisujemy liczby jedna pod drugą. Musimy pamiętać, aby rozszerzyć drugą liczbę o odpowiednią liczbę bitów. Miejsca te wypełniamy oczywiście zerami.
Dodajemy liczby tak, jakbyśmy dodawali normalne liczby pisemnie. Pamiętamy, że jeśli mamy 1+1, to wynikiem będzie 0 a jedynka przeniesie się do następnego rzędu (tak samo, jak przy dodawaniu normalnych liczb).
Co powiem o odejmowaniu? Wykonuje się je dokładnie tak samo, jak dodawanie. Tyle tylko, że dodajemy liczbę ujemną (z jedynką na początku). Spójrz na powyższy przykład. Możemy go interpretować jako 202+15=217 (co jest poprawną odpowiedzią, jeśli chodzi o NKB) lub jako -54+15=-39 w U2. Nie wierzysz? Zobacz sam – przelicz lub wbij w windowsowy kalkulator.
Wielkość zmiennej, wielkość rejestru
Za mało bitów!
Wszystkie dane, jakie wprowadzamy do komputera i na których komputer operuje, są przechowywane w formie zer i jedynek. Nie ważne, jaki to obiekt. Nie ważne, co on przedstawia. Jeśli to dźwięk – w najprostszej formie będzie on przechowywany jako próbki sygnału analogowego przekształcone za pomocą konwertera analogowo-cyfrowego do postaci liczby, która ostatecznie została zapisana w pamięci w formie zer i jedynek. Jeśli to obraz – najprościej możemy go przechowywać w formacie RGB, gdzie każdy bajt opisuje stopień „uczestnictwa” podstawowych barw w kolorze danego piksela. Podobnie jest ze wszystkimi innymi rzeczami.
No ale w tym miejscu powstaje pytanie – skąd komputer wie, że w tym miejscu kończy się np.: obraz a zaczyna dźwięk? Otóż, na każdy rodzaj danych w pamięci komputera przeznaczona jest odpowiednia ilość bitów (przypomnijmy – na jeden bajt składa się aż 8 bitów). Przykładowo, zmienna przechowująca liczbę całkowitą (int) w komputerze klasy PC ma 32 bity. Zmienna, przechowująca liczbę zmiennoprzecinkową o zwiększonej precyzji (double) ma tych bitów aż 64. Dodatkowo, w pamięci komputera każda z tych zmiennych znajduje się pod określonym adresem. To właśnie te dwie rzeczy pozwalają komputerowi interpretować poszczególne ciągi zer i jedynek.
Dla przyspieszenia obliczeń niektóre zmienne, na których często są wykonywane operacje, zapisywane są do tzw. „rejestrów”. Rejestrów w informatyce jest co najmniej kilka rodzajów, w zależności od tego, czym się zajmujemy. Ale jedno jest pewne – każdy rejestr ma swój określony rozmiar – w zależności od architektury – od 8 do 64 bitów. Załóżmy, że mamy do dyspozycji 8 bitów. Chcemy zapisać liczbę 240 (w systemie NKB). Wynik jest podany poniżej:
Jak pewnie zauważyłeś, nasza liczba zajmuje 8 bitów. Jak myślisz, jaką największą liczbę można zapisać na 8 bitach? Odpowiedź jest prosta – wystarczy przeliczyć liczbę 11111111 na system dziesiętny. Wynik to:
Przeprowadźmy teraz następujący eksperyment. Dodajmy binarnie liczbę 20 do liczby 240.
Czyli
Nasz wynik zajmuje 9 bitów. Jak więc zapisać 9 bitów informacji na 8 bitach, które mamy do dyspozycji? Nie ma rady, musimy utracić część informacji. W takim wypadku zawsze tracimy najstarsze (stojące po lewej stronie) bity. Podsumowując, wynik naszych obliczeń będzie następujący:
Czyli w systemie dziesiętnym:
Ciekawe, nie? I co najlepsze – jest to wynik prawidłowy! Komputer wykona dokładnie takie same obliczenia (spróbuj to zrobić na windowsowym kalkulatorze, wybierając „BYTE” jako rozmiar zmiennej). Powyżej przedstawioną operację możemy określić mianem „skracaniem rejestru”.
Rozszerzamy
Załóżmy, że w 8 bitowym rejestrze mamy zapisaną liczbę ?11110000?_NKB. Chcemy uniknąć błędu wspomnianego powyżej i przepisujemy liczbę do 16 bitowego rejestru, na którym będziemy wykonywali obliczenia. Ale powstaje pytanie, w które miejsce wstawić te 8 bitów i co wstawić w miejsce nowych? Po prostu postąpmy logicznie. Nasza liczba, jako pochodząca z mniejszego rejestru, powinna być zapisana na samym jego końcu. Natomiast pozostałe bity wypełniamy zerami. Poniżej, dla lepszego zobrazowania, znajduje się przykład:
A co, jeśli mamy liczbę ujemną, zapisaną w systemie U2? Tutaj sytuacja robi się nieco bardziej skomplikowana. W U2, jak wiemy, najbardziej znaczącym (najstarszym) bitem jest bit znaku. Cała zasada w systemie U2 polega na tym, że nadmiarowe miejsca wypełniamy bitem znaku. Z tego wynika, że w przypadku liczby dodatniej (bit znaku=0) nadmiarowe miejsca wypełniamy zerami. A w przypadku liczby ujemnej (bit znaku=1) jedynkami. Jeśli liczba jest ujemna, na początku zamiast zer dopisujemy jedynki. Przećwiczmy to na przykładzie. Załóżmy, że w rejestrze ośmiobitowym mamy zapisaną liczbę -35.
Przepisujemy teraz tę liczbę do rejestru 16 bitowego
System szesnastkowy, system ósemkowy
Na koniec wspomnijmy krótko o dwóch innych, dosyć często używanych w informatyce systemach liczbowych. Systemu szesnastkowego używa się najczęściej do zapisywania różnych adresów. System ósemkowy jest używany jeszcze rzadziej, np.: w linuksowym poleceniu chmod.
BIN->HEX System szesnastkowy, jak sama nazwa mówi, ma 16 cyfr. Wykorzystujemy zwykłe cyfry 0‑9 oraz litery A‑F. Odpowiednio: A=10,B=11,C=12,D=13,E=14,F=15. Zapiszmy sobie wszystkie 16 cyfr w systemie binarnym.
Nasuwa ci się coś na myśl? Skoro każdy znak w systemie szesnastkowym możemy opisać za pomocą 4 bitów, to najprostszym sposobem na przekształcenie liczby binarnej na system szesnastkowy jest podzielenie tej liczby na sekcje 4‑bitowe. Przetestujmy ten sposób. Przekształćmy liczbę 221 (DEC) na system szesnastkowy
Jeśli mamy mniejszą liczbę, np.: 11111, to nie podzielimy jej na sekcje 4‑bitowe. Ale nic straconego – stosując operację rozszerzania, możemy rozszerzyć ją do 8 bitów i wtedy wszystko ładnie się nam podzieli. BIN->OCT Przekształcenie z systemu binarnego na system ósemkowy wykonujemy analogicznie, jak wyżej. Lecz w tym wypadku liczbę binarną dzielimy na sekcje po trzy bity. (Dlaczego? Przeprowadź rozumowanie analogiczne jak powyżej, a się dowiesz) HEX->BIN Najprościej rzecz ujmując, HEX‑>BIN to odwrotność BIN‑>HEX. W BIN‑>HEX dzieliliśmy liczbę binarną na czterobitowe sekcje, aby zapisać litery. Natomiast tu litery przekształcamy, wg tabelki powyżej, na czterobitowe sekcje. OCT->BIN Konwersję przeprowadzamy analogicznie jak HEX‑>BIN. (Pamiętaj tylko, że w OCT każdy symbol jest opisany za pomocą trzech bitów!).
Podsumowując
Aby jak najlepiej przygotować się do kartkówki/kolokwium/czego tam chcesz, powyższe teorie trzeba wytrenować w praktyce. Masz narzędzie, które dostarcza praktycznie wszystkich opcji, których potrzebujesz – jest nim kalkulator windowsowy. Do dzieła! Nie będę określał, kiedy będzie następna część tej serii, ale z pewnością niedługo!
Adnotacja: W nowym silniku blogowym przydałaby się obsługa LaTeXu lub czegoś podobnego, po wstawianie wzorów jako print-screenów z Worda to masakra :(
Adnotacja 2: Mam nadzieję, że nie zrobiłem nigdzie błędów, ale tworzenie jakiegokolwiek materiału na 10 calowym netbooku z Intel Atomem jest raczej karą niż przyjemnością.