Przeczytaj
Na co dzień wykonujemy obliczenia w systemie dziesiętnym. Jesteśmy do tego tak przyzwyczajeni, że nie zastanawiamy się, jaką rolę odgrywają poszczególne cyfry, z których składa się dana liczba. Komputery natomiast wszystkie obliczenia wykonują w systemie binarnym. Jest to związane z tym, że elektroniczne układy pamięci mogą znajdować się w dwóch stanach, które umownie oznaczone są jako i .
Liczby przechowywane w pamięci komputera możemy podzielić na dwie główne kategorie: liczby całkowite i wymierne. Przyjrzyjmy się sposobom ich zapisywania. Zwróćmy również uwagę na problem zapisu znaku liczby oraz przecinka dziesiętnego – jak można zapisać je za pomocą zer i jedynek?
Sposób konwersji z systemu dziesiętnego na binarny omówiony jest szczegółowo w e‑materiale: Konwersja liczb z systemu dziesiętnego na dwójkowyKonwersja liczb z systemu dziesiętnego na dwójkowy, zaś dokładne przedstawienie systemu zapisu liczb ujemnych znajdziesz w e‑materiale Reprezentacja liczb ujemnych w systemie binarnymReprezentacja liczb ujemnych w systemie binarnym.
Liczby naturalne
Dla przykładu, przekształćmy liczbę dziesiętną do systemu dwójkowego. Chcemy ją przedstawić na czterech bitachbitach.
W zapisie pozostawiliśmy pierwszą cyfrę , ponieważ liczba miała zostać zapisana na bitach. Nie wpływa ona na wartość liczby i wydaje się zbędna, jednak musimy pamiętać, że komputer nie może pominąć żadnych bitów składających się na liczbę – dotyczy to również zer wiodących, które nie zmieniają jej wartości. Liczby w pamięci komputera przechowywane są na z góry określonej liczbie bitów, co ułatwia organizację pamięci. Każdy bit musi mieć wartość ustawioną na lub na – w przeciwnym razie nie wiedzielibyśmy, jaka wartość zostanie zapisana w komórce pamięci. Z tego powodu należy pamiętać o inicjowaniu zmiennych.
Przykładowo, jeżeli liczbę naturalną (bez znaku) będziemy przechowywali w postaci ciągu bitów, to w pamięci komputera liczbie dziesiętnej będą odpowiadać następujące cyfry:
W ten właśnie sposób komputery przechowują liczby naturalne. Maksymalna wartość zapisywanej liczby zależy od tego, ile bitów przeznacza się na jej przechowywanie. Ze względu na sposób, w jaki komputery odwołują się do danych w pamięci, rozmiary zmiennych podaje się w bajtachbajtach.
Jeden bajt jest równy bitom, zatem możemy zapisać:
najmniejszą wartość możliwą do zapisania na bitach: ,
największą wartość możliwą do zapisania na bitach: .
Ogólnie rzecz biorąc, na bitach można zapisać liczby naturalne (bez znaku) z przedziału od (gdzie wszystkie bity są ustawione na ) do (gdzie wszystkie bity ustawione są na ).
Liczba bitów w pamięci przydzielona różnego typu danym zależy od budowy komputera oraz użytego języka programowania i wykorzystywanego kompilatora. Tym samym typom danych mogą być na przykład przydzielane inne liczby bitów pamięci, zależnie od tego, w jakim języku pisany jest program. Z tego właśnie powodu, definiując zmienne danego typu, należy się upewnić, czy zakres wartości oferowanych przez ten typ jest wystarczający do konkretnego zastosowania.
Także różne wersje kompilatorów tego samego języka mogą różnić się między sobą. Zmiennym zostają przydzielone różne liczby komórek pamięci w sytuacjach, gdy jeden program jest pisany z myślą o komputerach stacjonarnych, a drugi ma być uruchamiany na mikrokontrolerze. Zazwyczaj opis standardu języka podaje minimalne rozmiary pamięci przeznaczonej dla zmiennych różnego typu.
Liczby całkowite ujemne
W jaki sposób zapisujemy liczbę ujemną? W powszechnie stosowanym systemie dziesiętnym liczba ujemna poprzedzona jest znakiem minus. Jak jednak zapisać ten znak w pamięci komputera, skoro mamy możliwość zapisywania informacji tylko w postaci zer i jedynek?
Metoda znak‑moduł
Pierwszy sposób jest dość prosty: należy zarezerwować jeden najbardziej znaczący bit liczby na przechowywanie znaku liczby. Przypisana mu wartość odpowiadałaby znakowi minus, zaś znakowi plus. Taka metoda, znana również jako „znak‑modułznak‑moduł”, zapisywana jest za pomocą skrótu w indeksie dolnym. Dla 4‑bitowej reprezentacji zachodzi wówczas:
Co do wartości bezwzględne liczby są równe.
Zaprezentowana metoda nie jest jednak używana w komputerach, ponieważ skutkuje problemami podczas wykonywania operacji matematycznych na liczbach zapisanych z jej zastosowaniem.
Kod uzupełnień
Drugi sposób jest nieco bardziej skomplikowany. Jest to kod uzupełnień do jednościkod uzupełnień do jedności (oznaczany skrótem ). Wartość ujemną liczby otrzymujemy przez negację wszystkich bitów liczby (czyli zamianę na , a na ). Oto przykład dla 4‑bitowej reprezentacji:
Metoda ta ma jednak pewną wadę: otóż w jej przypadku istnieją dwie reprezentacje liczby : zero ujemne i zero dodatnie. Zarówno , jak i mają wartość , lecz pierwszy bit może prowadzić do błędów w obliczeniach.
Podobnie jak w pierwszej metodzie, zakres liczb możliwych do zapisania w jednym bajcie to przedział od do .
Kod uzupełnień do dwóch
Najczęściej wykorzystywanym w komputerach sposobem zapisywania ujemnych liczb całkowitych jest zastosowanie kodu uzupełnień do dwóchkodu uzupełnień do dwóch (skrót ). Notacja taka jest podobna do zwykłego zapisu liczb w systemie binarnym, jednak najbardziej znaczący bit odpowiada potędze liczby ze znakiem minus. Oznacza to, że jeżeli mamy zapisać liczbę na bitach, będą odpowiadały im następujące wartości:
Miejsce numerowane | Wartość |
---|---|
Przyjrzyjmy się kilku liczbom zapisanym przy wykorzystaniu kodu uzupełnień do dwóch na bitach:
System dziesiętny | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Kod uzupełnień do 2 |
W przypadku zastosowania takiej metody nie pojawia się problem zera dodatniego i ujemnego, co znacznie upraszcza wykonywanie operacji arytmetycznych. Jedynym sposobem, aby za pomocą tego systemu zapisać liczbę , jest pozostawienie wszystkich bitów równych .
System ten daje:
najmniejszą wartość możliwą do zapisania na bitach: ,
największą wartość możliwą do zapisania na bitach: .
Stałoprzecinkowa reprezentacja liczb wymiernych
Ograniczmy rozważania do liczb wymiernych, czyli takich, które można zapisać za pomocą ilorazu dwóch liczb całkowitych, z których dzielnik musi być liczbą różną od zera (ponieważ rozwinięć liczb niewymiernych do ich dokładnej wartości dziesiętnej nie możemy zapisać). Nawet to okazuje się problematyczne – prędzej czy później będziemy musieli operować na przybliżeniach, w zależności od tego, jak dokładny będzie nasz system.
Liczby stałoprzecinkowe w systemie dziesiętnym
Zanim przejdziemy do omówienia liczb stałoprzecinkowych w systemie binarnym, zajmijmy się tym zagadnieniem w systemie dziesiętnym. Liczba stałoprzecinkowa to nic innego niż zestawienie ze sobą części całkowitej i ułamkowej, a następnie oddzielenie ich przecinkiem.
W systemach pozycyjnych (takich jak system dziesiętny) kolejne cyfry części całkowitej liczby mają przyporządkowane odpowiadające im wagi (kolejne potęgi podstawy systemu). Ponieważ w przypadku systemu dziesiętnego podstawą jest liczba dziesięć, najmniej znacząca cyfra odpowiada potędze – jest to cyfra jedności. Dalej mamy , następnie – cyfry odpowiednio dziesiątek i setek. Regułę tę powtarzamy, idąc dalej w lewą stronę liczby.
Część ułamkowa również spełnia wskazaną regułę, lecz kolejne cyfry na prawo od przecinka odpowiadają ujemnym potęgom. Oznacza to, że pierwsza cyfra po przecinku ma wagę , kolejna to ...
Ta zasada zachodzi w dowolnym systemie pozycyjnym.
Liczby stałoprzecinkowe, jak nazwa wskazuje, charakteryzują się stałym położeniem przecinka dziesiętego. Oznacza to, że z góry musimy zaplanować, z ilu cyfr będzie się składała część całkowita, a z ilu ułamkowa. W razie potrzeby obie części zostają dopełnione zerami.
Przykładowo, jeżeli na część ułamkową przeznaczymy trzy cyfry, a na całkowitą dwie, możemy tu zapisać liczby takie, jak ; czy .
Co jednak zrobić w sytuacji, gdy liczba nie może zostać zapisana jako stałoprzecinkowa? Zależy to od rodzaju problemu:
jeżeli liczba jest za duża - nie możemy zrobić niestety nic; implementacje takich liczb zazwyczaj informują użytkownika, że wystąpił błąd.
jeżeli liczba ma zbyt wiele cyfr po przecinku - używamy zasad zaokrąglania. Korzystając nadal z poprzedniego systemu, liczbę przybliżylibyśmy do . Może zdarzyć się też sytuacja, w której liczba po przecinku będzie zbyt mało znacząca, np. liczba zostanie przybliżona do .
W systemie stałoprzecinkowym dziesiętnym liczby ujemne zapisujemy z minusem poprzedzającym liczbę, np. .
Liczby stałoprzecinkowe w systemie binarnym
Podobnie jak w przypadku systemu dziesiętnego, najprostszy sposób zapisania liczby rzeczywistej w systemie to „sklejenie” dwóch liczb całkowitych. Pierwsza z nich reprezentuje część całkowitą, a druga – ułamkową. Za przykład niech posłuży liczba .
Zauważmy, że ułamki binarne tworzymy tak samo jak ułamki dziesiętne – kolejne cyfry odpowiadają mnożnikom ujemnych potęg podstawy systemu. Pierwsza cyfra po przecinku w systemie dziesiętnym jest mnożona przez , natomiast w systemie binarnym wynosi .
Aby zapisać ujemną liczbę rzeczywistą za pomocą reprezentacji stałoprzecinkowej, posłużymy się kodem uzupełnień do dwóch. Na zapis części całkowitej poświęcimy bit, zaś na zapis części ułamkowej bity. Kolejnym cyfrom będą odpowiadały następujące wartości:
Miejsce numerowane | Wartość |
---|---|
Spróbujmy za pomocą tego systemu zapisać liczbę . Ponieważ jest to liczba ujemna, pierwszy bit będzie równy . Aby otrzymać liczbę , zapiszemy za pomocą systemu binarnego wynik równania . Widzimy, że . Zapisując ten ułamek za pomocą systemu binarnego, otrzymujemy kolejno:
, zatem bit na pozycji drugiej będzie równy ,
, zatem bit na pozycji trzeciej będzie równy ,
, zatem bit na pozycji czwartej będzie równy ,
na koniec otrzymaliśmy liczbę , więc liczba została dokładnie zapisana.
Zapis binarny liczby w reprezentacji stałoprzecinkowej będzie więc równy .
Ograniczeniem tej reprezentacji jest oczywiście fakt, że przecinek zawsze musi znajdować się w tym samym miejscu. Pozycję separatora części całkowitej i ułamkowej trzeba dobrać tak, aby zakres reprezentowanych liczb spełniał oczekiwania użytkownika komputera.
Rozważmy teraz sytuację, w której nie będziemy w stanie zapisać danej liczby w sposób dokładny. Użyjmy tego samego systemu, aby zapisać liczbę .
, zatem pierwszy bit będzie równy ,
, zatem drugi bit będzie równy ,
, zatem trzeci bit będzie równy ,
, zatem czwarty bit będzie równy ,
nie otrzymaliśmy na koniec liczby , jednak wypełniliśmy wszystkie dostępne bity.
Zapis binarny liczby w tym systemie jest więc równy .
Przedstawiona metoda notacji jest dość prosta do wdrożenia i była stosowana w starszych komputerach, które nie miały dużej mocy obliczeniowej. Do wykonywania operacji matematycznych na liczbach stałoprzecinkowych może zostać wykorzystany system używany do działań na liczbach całkowitych. System ten jest też do dziś stosowany w układach, w których zasoby takie jak miejsce ograniczone.
Zmiennoprzecinkowa reprezentacja liczb rzeczywistych w systemie dziesiętnym
Reprezentacja zmiennoprzecinkowa liczby rzeczywistej w systemie dziesiętnym nosi również nazwę postaci wykładniczej. Ogólna postać liczby zapisanej w tym systemie to:
to znak plus (oznaczany jako , zazwyczaj pomijany) lub minus (zapisywany jako ), oznacza mantysęmantysę, zaś - cechęcechę. Ważną operacją związaną z mantysą jest jej normalizacja. Bez niej ten sposób zapisu nie byłby jednoznaczny – jedna liczba rzeczywista w systemie dziesiętnym może mieć bowiem więcej niż jeden sposób zapisu w reprezentacji zmiennoprzecinkowej. Przykładowo liczbę oznaczałby zarówno zapis , jak i zapis .
Często spotykany jest również sposób zapisu , gdzie oznacza wykładnik (z ang. exponent). Nie jest to podstawa logarytmu naturalnego. Przykładowy zapis jest równoważny z zapisem .
Mantysa jest znormalizowana, jeżeli należy do przedziału , gdzie to podstawa systemu. Dla systemu dziesiętnego wynosi . Oznacza to, że znormalizowana mantysa jest liczbą naturalną z przedziału od 1 do 9. Przykładem znormalizowanej mantysy są liczby: ; . Mantysami, które nie są znormalizowane, są np. liczby: ; ; .
Teraz rozpatrzmy przykładową liczbę zapisaną w reprezentacji zmiennoprzecinkowej. Weźmy liczbę:
W tej sytuacji przecinek należy przesunąć w prawo o miejsca, w wyniku czego otrzymamy liczbę .
Przecinek należy przesunąć w lewo w sytuacji, gdy cecha jest ujemna. Przykładowo, liczba wymaga przesunięcia przecinka o miejsce w lewo, dzięki czemu otrzymamy liczbę .
Jeżeli cecha jest równa , to wyrażenie przyjmuje wartość . Oznacza to, że przykładowa liczba zapisuje się jako .
Proces normalizacji mantysy sprowadza się do przesuwania przecinka oraz jednoczesnej zmiany cechy tak, aby wartość liczby się nie zmieniła. Oznacza to, że jeżeli przecinek przesuwamy w prawo, to cecha zmniejsza się o . Jeśli zaś przecinek przesuwamy w lewo – cecha zwiększa się o .
Rozpatrzmy liczbę z nieznormalizowaną mantysą . Aby ją znormalizować, przecinek należy przesunąć o miejsce w lewo, zatem cecha musi zostać zwiększona o . Otrzymujemy znormalizowaną postać .
Weźmy inną liczbę: . Również jest ona w postaci nieznormalizowanej. Aby to naprawić, przecinek musimy przesunąć o miejsca w prawo, zatem cecha musi zostać zmniejszona o . Otrzymujemy tym samym liczbę .
Zmiennoprzecinkowa reprezentacja liczb rzeczywistych w systemie binarnym
We współczesnych komputerach znacznie powszechniejsza od stałoprzecinkowej jest reprezentacja zmiennoprzecinkowa. Zapewnia on większą dokładność (kosztem zwiększenia złożoności obliczeń i trudności w budowie procesora). Ta notacja okazuje się najbardziej uniwersalna – pozwala ona na zapisywanie zarówno liczb bardzo dużych, jak i bardzo małych.
Aby przedstawić liczbę binarną w postaci zmiennoprzecinkowej, dzielimy ją na dwie części: mantysę oraz cechę.
Oto liczba binarna w reprezentacji zmiennoprzecinkowej:
Mantysa zapisana jest w postaci znormalizowanej. W systemie binarnym oznacza to, że jej część całkowita jest równa , zaś dalej znajduje się dowolna część ułamkowa. Ponieważ część całkowita zawsze wynosi , w zapisie często jest ona pomijana, a w zapisie binarnym widnieje tylko część ułamkowa. Zauważmy, że tutaj podstawą potęgi jest liczba , czyli podstawa systemu liczbowego.
Symbole , , to – odpowiednio – znak, mantysa oraz cecha. Liczba bitów przypadających na poszczególne elementy jest z góry ustalona. Posłużmy się przykładem liczby 8‑bitowej:
Dla lepszej czytelności oddzieliliśmy od siebie poszczególne elementy liczby. Pierwszy bit odpowiada znakowi: ponieważ jest równy , znakiem jest minus. Następne bity to cecha (czyli wykładnik potęgi zapisany w kodzie uzupełnień do dwóch) – jest ona równa . Na końcu znajduje się mantysa, której wartość, po dodaniu niewidocznej części ułamkowej, wynosi . Mantysa jest zawsze dodatnia (już przechowujemy znak na innym bicie).
Możemy teraz policzyć wartość liczby rzeczywistej w systemie dziesiętnym:
W taki sposób nie możemy jednak zapisać bezpośrednio liczby – często przyjmuje się, że jeżeli wszystkie bity cechy są równe , liczba ma zostać potraktowana jako .
Zamiana liczby rzeczywistej na reprezentację zmiennoprzecinkową
Aby zmienić liczbę na reprezentację zmiennoprzecinkową, zaczynamy od zapisania jej wartości bezwzględnej w postaci binarnej. Jeżeli liczba nie ma skończonego rozwinięcia binarnego, zapisujemy ją w taki sposób, aby łączna liczba cyfr w jej zapisie była o dwa większa od liczby bitów poświęconych na mantysę. W omawianym wypadku będzie to cyfr, ponieważ na mantysę przeznaczymy cyfry (tak jak w przykładzie poprzednim). Cecha zostanie zapisana na bitach.
Przykładowo, dla liczby :
Następnie mantysę trzeba znormalizować. Oznacza to, że jej część całkowita musi być równa . Ponieważ przyjęto, że mantysy zawsze są zapisane w postaci znormalizowanej, ten bit jest pomijany w zapisie, mimo że domyślnie się tam znajduje.
Aby znormalizować mantysę, będziemy przesuwali przecinek dziesiętny w lewą lub prawą stronę, jednocześnie zmieniając odpowiednio potęgę liczby .
Przykładowo, dla liczby otrzymamy kolejno:
Tak otrzymaną mantysę należy zaokrąglić, aby jej część ułamkowa mogła zostać zapisana na bitach. Jeżeli na piątym miejscu po przecinku znajduje się cyfra , jej zaokrągleniem będą cztery pierwsze cyfry po przecinku. W przeciwnym razie do czterech cyfr po przecinku należy dodać jedną część dziesięciotysięczną.
Teraz możemy zapisać liczbę za pomocą reprezentacji zmiennoprzecinkowej. Wartością cechy jest potęga liczby , jaką otrzymaliśmy w trakcie normalizowania mantysy.
Znak rozważanej liczby to minus, więc pierwszy bit będzie równy . Cechą liczby jest , zatem kolejne bity będą równe . Mantysa tej liczby to , więc ostatnie bity będą równe .
Zapis omawianej liczby to . Jej wartość w systemie dziesiętnym możemy obliczyć w następujący sposób:
Słownik
najmniejsza adresowalna jednostka informacji pamięci komputerowej, składająca się zazwyczaj z bitów
najmniejsza jednostka informacji zapisanej w pamięci komputera; przyjmuje jedną z dwóch wartości, które zwykle określa się jako i
w systemie binarnym wykładnik potęgi liczby (w innych systemach – wykładnik potęgi podstawy systemu), przez którą należy pomnożyć wartość mantysy
metoda zapisu liczb całkowitych, w której najstarszy bit odpowiada ujemnej potędze liczby ; liczby nieujemne zapisywane są jak w naturalnym kodzie binarnym
metoda zapisu liczb całkowitych, w której liczby dodatnie zapisywane są w naturalnym kodzie binarnym, natomiast liczby ujemne uzyskuje się poprzez negację wszystkich bitów; najstarszy bit jest bitem znaku, czyli wartość oznacza liczbę nieujemną, a wartość liczbę niedodatnią
część ułamkowa liczby zmiennoprzecinkowej
sposób zapisu liczb całkowitych, w którym najstarszy bit odpowiada za znak liczby – jeżeli ma wartość , to liczba całkowita jest nieujemna, a jeśli wartość , to liczba jest niedodatnia; pozostałe bity odpowiadają wartości bezwzględnej liczby w naturalnym kodzie binarnym