Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych Autor artykułu: mgr Jerzy Wałaszek |
©2017 mgr
Jerzy Wałaszek
|
Liczby całkowiteJęzyk C++ zawiera cały szereg typów danych dla liczb całkowitych. Podstawowym typem jest typ int. Odzwierciedla on typowe dane całkowite dla danej architektury. Używana przez nas wersja Code::Blocks pracuje w architekturze 32-bitowej, zatem typ int będzie się odnosił do danych 32-bitowych. Aby sprawdzić rozmiar typu int, uruchom poniższy program:
Jeśli otrzymasz wynik 4, to znaczy, że standardowy typ całkowity int zajmuje w pamięci komputera 4 bajty, czyli 4 x 8 = 32 bity. Typ int odnosi się do danych zapisanych w 32-bitowym kodzie U2. Zakres możliwych wartości zawiera się w przedziale od -231 do 231 - 1, czyli od -2147483648 do 2147483647, zapamiętaj: +/- 2 miliardy. Jeśli poprzedzimy typ int modyfikatorem unsigned, to otrzymamy typ całkowity dla liczb dodatnich i zera. Dana typu unsigned int (int można pominąć) ma ten sam rozmiar co typ int, czyli w naszym przypadku 4 bajty, 32 bity. Typ unsigned oznacza 32-bitową daną w naturalnym kodzie binarnym o zakresie wartości od 0 do 232 - 1, czyli od 0 do 4294967297, zapamiętaj: 0...4 miliardy. Typu unsigned używamy, gdy nasz program nie operuje na liczbach ujemnych. W takim przypadku mamy większy zakres liczb dodatnich. Oprócz modyfikatora unsigned do typu int możemy dodać:
Jako liczby całkowite można również używać kody znaków ASCII, które są 8-bitowe. Podstawowy typ znakowy (kod jednego znaku) w C++ nosi nazwę char. Oznacza on 8-bitową liczbę U2 o zakresie od -27 do 27-1, od -128 do 127. Typ unsigned char oznacza 8-bitową liczbę w kodzie NBC o zakresie od 0 do 28-1, od 0 do 255. Typy danych na różnych platformach mogą mieć różny rozmiar w pamięci. Dlatego rozpoczynając pracę z nowym środowiskiem C++ dobrze jest je sobie sprawdzić, np. poniższym programem:
Podsumujmy typy całkowite dla Code::Blocks:
Najczęściej będziemy używać typu int oraz unsigned. Pozostałe typy używa się w wyjątkowych sytuacjach.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
Liczby zmiennoprzecinkoweW obliczeniach naukowych musimy operować wartościami niecałkowitymi. Dlatego język C++ został wyposażony w nowy typ danych – liczby rzeczywiste. Zanim do nich przejdziemy, zapoznajmy się ze sposobami zapisu liczb rzeczywistych w komputerze. Odpowiedzmy sobie na proste pytanie – czy wykorzystując tylko liczby całkowite, można zapisywać również liczby ułamkowe? Na fizyce zapewne spotkałeś się wielokrotnie z zapisem bardzo dużych lub bardzo małych wartości. Robiono to na przykład tak:
2,5 × 1036
lub tak:
3,3 × 10-31
Ogólnie możemy to zapisać następująco:
W = m × pc gdzie: W – wartość liczby
Podstawa p jest zwykle stała, w systemie dziesiętnym wynosi 10.
Zatem nie musimy jej zapamiętywać – po prostu wiemy, że wynosi 10. Pozostają do
zapamiętania dwie pozostałe liczby:
Przykłady: c = 1
Jeśli naszą liczbę zapiszemy w postaci pary liczb (c,m), to następujące pary odpowiadają tej samej wartości W: (3,1) (30,0) (0,3 2). Cecha jest zawsze liczbą całkowitą, a te już znamy. Mantysa jest liczbą ułamkową. Aby standaryzować zapis, możemy się umówić, że mantysa jest ZAWSZE ułamkiem właściwym o mianowniku np. 1000. Skoro tak, to licznik tego ułamka jest liczbą całkowitą od -999 do 999. Skoro mianownik jest ustalony, to nie musimy go zapamiętywać – wystarczy nam licznik, aby odtworzyć wartość mantysy. Przykład: Niech m oznacza u nas licznik mantysy, mianownik mantysy ma stałą wartość 1000 (bo tak się umawiamy).
c = 2
Pomimo iż pamiętamy jedynie liczby całkowite, wartość reprezentowanej przez nie liczby jest ułamkowa. Zatem liczby rzeczywiste możemy zapamiętywać w postaci pary dwóch liczb całkowitych – cechy oraz licznika mantysy. Tak właśnie komputer zapamiętuje liczby rzeczywiste – w postaci dwóch liczb całkowitych, które wspólnie razem tworzą tzw. kod zmiennoprzecinkowy (ang floating point code). Komputer pracuje w systemie dwójkowym, zatem mantysa jest ułamkiem binarnym – mianownik jest zawsze potęgą liczby 2 (w systemie praktycznym jest to dosyć duża potęga, np. 253). Komputer w kodzie zmiennoprzecinkowym zapamiętuje jedynie licznik tego ułamka. Podstawa p wynosi 2. Znając mianownik ułamka mantysy 2x oraz jego licznik m i cechę c możemy wyliczyć wartość dowolnej liczby zmiennoprzecinkowej:
W = m / 2x × 2c m – licznik mantysy, liczba całkowita
Przykład: Niech mianownik ułamka mantysy wynosi 16 (24). Zatem licznik może przybierać wartości od -16 do 15. Obliczmy wartości kilku binarnych liczb zmiennoprzecinkowych:
c = 2
c = 1
Widzimy, że obliczenia nie są skomplikowane. Wyznaczenie licznika mantysy m i cechy c jest równie proste. Wykorzystujemy tu proste przekształcenia matematyczne w celu sprowadzenia mantysy do ułamka właściwego. Obliczmy przykładowo m i c dla liczby 2,5.
Najpierw zapisujemy liczbę 2,5 w postaci ułamka o mianowniku 16:
2,5 = 5/2 = 40/16
Teraz zapisujemy mantysę i cechę wstępną równą 0:
3,5 = 40/16 × 20
Mantysę musimy sprowadzić do ułamka właściwego. Zatem licznik dzielimy przez 2, a do cechy dodajemy 1. Operację tę kontynuujemy do momentu, aż mantysa stanie się ułamkiem właściwym:
40/16 × 20 = 20/16 × 21 = 10/16 × 22.
Dostaliśmy:
c = 2 i m = 10.
Pewnych liczb nie da się dokładnie przedstawić w tym systemie. Spróbujmy zapisać liczbę 17 przy założeniu, że mantysa jest ułamkiem właściwym o mianowniku 16.
17 = 272/16 × 20 = 136/16 × 21 = 68/16 × 22 = 34/16 × 23 = 17/16 × 24 = 8/16 × 25
W ostatnim działaniu przyjęliśmy 8 jako wynik dzielenia 17 przez 2, ponieważ licznik ułamka musi być liczbą całkowitą. To zaokrąglenie spowodowało, że pierwotna liczba 17 zostaje sprowadzona do liczby: c = 5
Wniosek – jeśli mantysa jest ułamkiem o mianowniku 16, to liczby 17 nie da się przedstawić dokładnie. Gdyby mantysa była ułamkiem o mianowniku większym, np. 32, problem ten nie wystąpiłby dla liczby 17, ale pojawiłby się z kolei dla liczby 33. Zatem jest to stała cecha tego zapisu. W rzeczywistym systemie mantysa jest ułamkiem o mianowniku bardzo dużym, np. 256. Pozwala to zapisywać liczby z dużą dokładnością. Ale błędy zaokrągleń dają czasami znać o sobie, co zobaczymy w dalszych przykładach. Więcej informacji na ten temat znajdziesz w artykule o binarnym kodowaniu liczb.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
Zmiennoprzecinkowe typy danych
W języku C++ stosowane są trzy typy danych zmiennoprzecinkowych. Różnią się one
tzw. precyzją, czyli dokładnością zapisu liczb. Wiąże się to z ilością bitów,
które w kodzie zmiennoprzecinkowym są przeznaczone na zapis licznika mantysy. Im
więcej bitów, tym ułamek mantysy może dokładniej reprezentować w zapisie
zmiennoprzecinkowym daną wartość. Typy zmiennoprzecinkowe są następujące:
float – dane zmiennoprzecinkowe 32 bitowe, pojedynczej precyzji. Dokładność 7-8 cyfr znaczących. double – dane zmiennoprzecinkowe 64 bitowe podwójnej precyzji. Dokładność 15 cyfr znaczących. long double – dane zmiennoprzecinkowe 80 bitowe o rozszerzonej precyzji. Dokładność 20 cyfr znaczących.
Typ float jest najmniej dokładnym typem danych rzeczywistych. Jedyną jego zaletą jest mały rozmiar – 32 bity. Dzisiaj nie zaleca się jego stosowania. Typ double jest standardowym typem rzeczywistym. Jeśli nie będą istniały specjalne powody, to będziemy stosować w programach tylko typ double. Typ long double jest typem danych, które wewnętrznie wykorzystuje koprocesor arytmetyczny – jest to część procesora Pentium wykonująca operacje zmiennoprzecinkowe. Typ ten pozwala zminimalizować błędy zaokrągleń i zachować dużą precyzję obliczeń. Jednakże nie będziemy z niego korzystać, ponieważ może nie być dostępny na innych platformach sprzętowych – koprocesory mają różne standardy w różnych systemach.
Powyższy program, chociaż działa doskonale posiada kilka wad z punktu widzenia użytkownika. Uruchom go i wprowadź poniższe dane:
Zwróć uwagę, że wynik nie jest odpowiednio wyrównany. Kropki dziesiętne znajdują się w różnych kolumnach. Liczba miejsc po przecinku jest różna. A teraz wpisz takie dane:
Ponieważ wynik jest dużą liczbą, to komputer przedstawia go w postaci naukowej
Aby mieć pełną kontrolę nad sposobem prezentacji liczb zmiennoprzecinkowych przez konsolę, musimy użyć tzw. manipulatorów strumienia. W tym celu należy do programu dołączyć plik nagłówkowy iomanip, który zawiera definicję tych manipulatorów. Manipulatory przesyłamy do strumienia jak zwykłe dane. W poniższej tabelce zebraliśmy podstawowe manipulatory strumienia cout.
Nasz program po zastosowaniu manipulatorów wygląda następująco:
Liczby zmiennoprzecinkowe są liczbami przybliżonymi. Typ double pozwala reprezentować dokładnie tylko 15 cyfr znaczących. Jeśli liczba ma ich więcej, to tylko 15 pierwszych cyfr będzie dokładne. Pozostałe już nie.
Niektóre liczby nie będą nigdy reprezentowane dokładnie, chociaż posiadają mniej niż 15 cyfr znaczących. Typowym przykładem jest ułamek 0,1. Ponieważ mantysa liczby zmiennoprzecinkowej jest ułamkiem dwójkowym (mianownik tego ułamka jest potęgą liczby 2), to wartości 0,1 nie da się nigdy przedstawić dokładnie, zawsze będzie istniał pewien błąd. Poniższe ułamki dwójkowe są prawie równe 0,1. Ale "prawie" nie oznacza wcale, że są równe:
1/8, 1/16, 3/32, 6/64, 12/128, 25/256, 102/1024, 6553/65535 ...
Ułamek dziesiętny 0,1 posiada w systemie dwójkowym nieskończone rozwinięcie. W naszym systemie dziesiętnym podobną własność mają ułamki 1/3, 1/6, 1/7, 1/9 – ułamków tych nie da się przedstawić dokładnie za pomocą skończonej ilości cyfr w systemie dziesiętnym. Tak samo w systemie dwójkowym, ułamka 1/10 nie da się przedstawić dokładnie za pomocą mantysy o skończonej liczbie bitów. Konsekwencje tego faktu prezentuje poniższy prosty program:
Program dodaje do zmiennej x ułamek 0,1. Po wykonaniu 9 takich dodawań x powinno osiągnąć wartość 1 i na ekranie powinien pojawić się tekst Dobrze!. Tymczasem po uruchomieniu programu pojawia się ten drugi tekst, pomimo że program wyświetla wartość x jako 1. Powodem jest to, iż po wykonaniu 9 dodawań 0,1 w zmiennej x nie była dokładnie wartość 1, tylko wartość bardzo zbliżona do 1. Niestety, operator == traktuje ją jako różną od 1. Z programu powyższego wynika BARDZO WAŻNY wniosek – liczb zmiennoprzecinkowych NIE WOLNO przyrównywać do wartości dokładnych. Zamiast sprawdzania, czy dwie liczby zmiennoprzecinkowe a i b są równe:
a == b
powinniśmy zbadać ich różnicę. Jeśli ta różnica jest dostatecznie mała, to przyjmiemy, że liczby a i b są równe. Różnica może być dodatnia lub ujemna. Aby nie rozważać zatem dwóch różnych przypadków, będziemy badać wartość bezwzględną różnicy:
| a - b | < wartość graniczna
Wartość bezwzględna liczby zmiennoprzecinkowej oblicza funkcja fabs(x). Dostęp do tej funkcji uzyskamy po dołączeniu do programu pliku nagłówkowego cmath. Za wartość graniczną przyjmiemy 0.0000001. W tym celu w programie zdefiniujemy stałą EPS o takiej właśnie wartości. W pętli while mamy warunek x różne od 1. Otrzymamy go następująco:
fabs(x - 1) > EPS
A oto zmodyfikowany program, który teraz działa wg oczekiwań:
Na liczby zmiennoprzecinkowe trzeba bardzo uważać w programowaniu. Musimy pamiętać, że są to wartości przybliżone. Błąd w stosunku do wartości dokładnej jest zwykle bardzo mały, ale może powodować błędne działanie programu, jeśli nie weźmiemy tego faktu pod uwagę. Następny program znajduje pierwiastki równania kwadratowego:
Algorytm znajdowania pierwiastków jest następujący:
Obliczamy wyróżnik równania:
W zależności od wartości wyróżnika mamy trzy przypadki:
Istnieje pierwiastek podwójny, który obliczamy ze wzoru:
Istnieją dwa różne pierwiastki, które obliczamy ze wzorów:
Nie ma pierwiastków rzeczywistych.
Algorytm wymaga obliczania pierwiastka kwadratowego, który udostępni nam funkcja sqrt(x), dostępna po dołączeniu pliku nagłówkowego cmath.
Program sprawdź dla trzech poniższych równań kwadratowych:
x2 - 2x + 1 = 0,
pierwiastek podwójny x1 = x2 = 1
|
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe