Podręcznik
2. Wbudowane typy danych
2.4. Typy zmiennoprzecinkowe
Typ zmiennoprzecinkowy w języku C++ służy do zapisywania liczb rzeczywistych. Liczby rzeczywiste odgrywają istotną rolę w obliczeniach naukowych, inżynierskich oraz w wielu aplikacjach, w których kluczowym aspektem jest dokładność. W tym przypadku dokładność zapewniana przez liczby całkowite nie jest wystarczająca. W języku C++ dostępne są trzy główne typy zmiennoprzecinkowe: float
, double
oraz long double
. Każdy z wymienionych typów zmiennych różni się precyzją i zakresem wartości, które może przechowywać.
Poza standardowym sposobem zapisu liczb tzn. 10, 200.46 możemy zapisywać liczby używając notacji naukowej. Notacja naukowa jest sposobem reprezentowania bardzo dużych lub bardzo małych liczb w zwięzły sposób. Jest szczególnie użyteczna w naukach ścisłych i inżynierii, gdzie liczby mogą mieć wiele cyfr znaczących i szeroki zakres wartości.
W notacji naukowej wartość liczby obliczamy zgodnie ze wzorem:
gdzie:
Przy pomocy wzoru możliwe jest wyznaczenie wartość liczby zmiennoprzecinkowej zapisanej w dowolnym systemie pozycyjnym, a nie tylko dziesiętnym. W przypadku dziesiętnego systemu .
Liczby wymierne i niewymierne wchodzą w skład zbioru liczb rzeczywistych, a ich reprezentacja za pomocą notacji naukowej (formy wykładniczej) stanowi oficjalną definicję liczb zmiennoprzecinkowych. W tym momencie warto zastanowić się w jaki sposób liczby zmiennoprzecinkowe są przechowywane w pamieci komputera. Oczywiście zapis jest binarny. W łatwy sposób można zapisać część całkowitą, ale w jaki sposób zapisać część dziesiętną? Najlepiej będzie zaprezentować to na przykładzie.
Kolejnym krokiem jest przedstawienie w postaci binarnej liczby 0.125. W tym celu należy wykonać działania przedsatwione w tabeli, które polegają na kolejnym mnożeniu liczb przez 2 oraz zapisie części całkowitej w postaci binarnej.
Liczba wejściowa (ułamkowa) | Wynik mnożenia przez 2 | Wartość całkowita wyniku |
---|---|---|
0.125 | 2*0.125 = 0.250 | 0 |
0.250 | 2*0.250 = 0.500 | 0 |
0.500 | 2*0.500 = 1.000 | 1 |
Proces mnożenia kończy się gdy część ułamkowa równa się 0.
Liczbę zmiennoprzecinkową możemy zapisać jako 101.001.
Należy zwrócić uwagę, że istnieją przypadki w których proces mnożenia może nigdy się nie kończyć. Mamy wtedy do czynienia z liczbami nieskończonymi. W przypadku liczby nieskończonej otrzymujemy pewną powtarzającą się sekwencję binarnych wartości. Mówimy wtedy, że wartość jest w okresie. Możemy to zaobserwować na przykładzie liczby 0.33.
Liczba wejściowa (ułamkowa) | Wynik mnożenia przez 2 | Wartość całkowita wyniku |
---|---|---|
0.33 | 2*0.33 = 0.66 | 0 |
0.66 | 2*0.66 = 1.32 | 1 |
0.32 | 2*0.32 = 0.64 | 0 |
0.64 | 2*0.64 = 1.28 | 1 |
0.28 | 2*0.28 = 0.56 | 0 |
0.56 | 2*0.56 = 1.12 | 1 |
0.12 | 2*0.12 = 0.24 | 0 |
0.24 | 2*0.24 = 0.48 | 0 |
0.48 | 2*0.48 = 0.96 | 0 |
0.96 | 2*0.96 = 1.92 | 1 |
0.92 | 2*0.92 = 1.84 | 1 |
Zbierając wszystkie części całkowite uzyskane w kolejnych krokach, otrzymujemy:
Reprezentacja binarna liczby jest nieskończona i okresowa. W praktyce, można ją przybliżyć do określonej liczby miejsc po przecinku, ale zawsze będzie to tylko przybliżenie. Na przykład:
W celu ujednolicenia sposobu zapisu liczb zmiennoprzecinkowych został wprowadzony standard IEEE754. Standard IEEE 754 to zestaw specyfikacji definiujących sposób reprezentacji oraz operacji na liczbach zmiennoprzecinkowych w komputerach. Opracowany przez Institute of Electrical and Electronics Engineers (IEEE), standard ten jest powszechnie używany we współczesnych systemach komputerowych i językach programowania, zapewniając jednolitą metodę manipulacji liczbami zmiennoprzecinkowymi, co umożliwia przenośność kodu między różnymi platformami.
Liczby zmiennoprzecinkowe w standardzie IEEE 754 są reprezentowane w postaci znormalizowanej za pomocą trzech głównych części:
- Znak (1 bit): Określa, czy liczba jest dodatnia (0) czy ujemna (1).
- Cecha (eksponenta): Przechowuje przesuniętą wartość wykładnika. Wartość ta jest zapisywana w postaci przesuniętej o wartość zwaną biasem.
- Mantysa (część ułamkowa): Przechowuje znaczące cyfry liczby.
W języku C++ możemy więc wyróżnić następujące typy liczb zmiennoprzecinkowych:
- Typ
float
to pojedyncza precyzja zmiennoprzecinkowa. Zwykle jest przechowywana na 32 bitach (4 bajty). Zakres liczb wynosi od 1.2E-38 do 3.4E+38, natomiast precyzja jest do 7 cyfr znaczących.
float zmienna_rzeczywista;
- Typ
double
to podwójna precyzja zmiennoprzecinkowa. Zwykle jest przechowywana na 64 bitach (8 bajtów). Zakres liczb wynosi od 2.2E-308 do 1.8E+308, natomiast precyzja jest do 15 cyfr znaczących.
double zmienna_rzeczywista;
- Typ
long double
może oferować jeszcze większą precyzję. Jego rozmiar zależy od implementacji, ale zazwyczaj jest przechowywany na co najmniej 80 bitach (10 bajtów) lub więcej.
long double zmienna_rzeczywista;