Wszechnica Wszechwiedzy - Baner

Arytmetyka zmiennoprzecinkowa – cecha, mantysa i zapis binarny

Komputer wykonuje obliczenia na liczbach zapisanych za pomocą skończonej liczby bitów. Oznacza to, że nie może dokładnie przechowywać wszystkich liczb rzeczywistych. Wiele wartości musi zostać zastąpionych najbliższą liczbą możliwą do przedstawienia w pamięci urządzenia.

Arytmetyka zmiennoprzecinkowa określa sposób zapisywania i przetwarzania takich przybliżonych wartości. Umożliwia reprezentowanie zarówno bardzo dużych, jak i bardzo małych liczb, ale wprowadza błędy zaokrągleń, ograniczoną liczbę cyfr znaczących oraz szczególne przypadki, takie jak nieskończoność i wartość NaN.

Zrozumienie arytmetyki zmiennoprzecinkowej jest szczególnie ważne w metodach numerycznych. Nawet prawidłowo dobrany algorytm może zwrócić niedokładny wynik, jeżeli występujące podczas obliczeń błędy zaokrągleń są wzmacniane przez kolejne działania.

Co oznacza zapis zmiennoprzecinkowy?

Nazwa „zmiennoprzecinkowy” oznacza, że położenie przecinka nie jest ustalone. Liczba jest przechowywana w sposób przypominający zapis wykładniczy stosowany w matematyce.

W systemie dziesiętnym możemy zapisać na przykład:

\[123\,450=1{,}2345\cdot 10^5\]

oraz

\[0{,}0012345=1{,}2345\cdot 10^{-3}.\]

W obu przypadkach część znacząca liczby jest taka sama, natomiast zmienia się wykładnik potęgi dziesięciu. Dzięki temu można zapisywać liczby o bardzo różnym rzędzie wielkości.

Komputer postępuje podobnie, lecz zamiast systemu dziesiętnego wykorzystuje system binarny. Ogólną postać znormalizowanej liczby zmiennoprzecinkowej można zapisać jako

\[x=(-1)^s\cdot m\cdot 2^e,\]

gdzie:

W literaturze informatycznej zamiast słowa „mantysa” często używa się dokładniejszego określenia znacząca lub angielskiego terminu significand. W potocznym opisie formatu IEEE 754 nadal powszechnie mówi się jednak o polu mantysy.

Znormalizowany zapis binarny

Podobnie jak w dziesiętnym zapisie naukowym, liczba binarna może zostać przedstawiona w postaci znormalizowanej. Przecinek przesuwa się tak, aby przed nim znalazła się dokładnie jedna niezerowa cyfra.

W systemie binarnym jedyną niezerową cyfrą jest \(1\), dlatego dodatnia liczba znormalizowana ma postać

\[1{,}b_1b_2b_3\ldots{}_2\cdot 2^e.\]

Przykładowo liczba binarna

\[101011{,}01_2\]

może zostać zapisana jako

\[1{,}0101101_2\cdot 2^5.\]

Przecinek przesunięto o pięć miejsc w lewo, dlatego wykładnik wynosi \(5\). Pierwsza jedynka w znormalizowanym zapisie jest zawsze obecna, więc nie trzeba zapisywać jej w pamięci. Nazywa się ją ukrytą jedynką.

Dzięki ukrytej jedynce liczba przechowywanych bitów części znaczącej jest o jeden mniejsza od rzeczywistej precyzji zapisu. W formacie pojedynczej precyzji przechowuje się 23 bity części ułamkowej, lecz znacząca ma efektywnie 24 bity.

Standard IEEE 754

Najczęściej stosowane zasady reprezentacji liczb zmiennoprzecinkowych określa standard IEEE 754. Definiuje on format zapisu liczb, wykonywanie działań arytmetycznych, sposoby zaokrąglania oraz wartości specjalne.

W typowym zapisie binarnym liczba jest dzielona na trzy pola:

Schemat 32-bitowej liczby zmiennoprzecinkowej IEEE 754 podzielonej na bit znaku, ośmiobitową cechę i dwudziestotrzybitową mantysę.
Liczba zmiennoprzecinkowa pojedynczej precyzji składa się z jednego bitu znaku, ośmiu bitów cechy oraz 23 bitów mantysy.

Pojedyncza precyzja

Format pojedynczej precyzji, określany również jako binary32 lub często jako typ float, wykorzystuje 32 bity:

Wartość przesunięcia wykładnika wynosi w tym formacie

\[\operatorname{bias}=127.\]

Jeżeli w polu cechy zapisano wartość \(E\), rzeczywisty wykładnik liczby znormalizowanej wynosi

\[e=E-127.\]

Format pojedynczej precyzji zapewnia około 7 cyfr dziesiętnych dokładności. Największa skończona dodatnia liczba ma wartość około

\[3{,}4028235\cdot 10^{38}.\]

Podwójna precyzja

Format podwójnej precyzji, określany jako binary64 lub zwykle jako typ double, wykorzystuje 64 bity:

Przesunięcie wykładnika wynosi

\[\operatorname{bias}=1023.\]

Podwójna precyzja zapewnia około 15 – 16 poprawnych cyfr dziesiętnych. Jest najczęściej stosowana w obliczeniach naukowych i numerycznych, ponieważ znacznie ogranicza błędy zaokrągleń w porównaniu z pojedynczą precyzją.

WłaściwośćPojedyncza precyzjaPodwójna precyzja
Liczba bitów3264
Bit znaku11
Cecha8 bitów11 bitów
Mantysa23 bity52 bity
Efektywna precyzja znaczącej24 bity53 bity
Przesunięcie wykładnika1271023
Przybliżona dokładność dziesiętnaokoło 7 cyfrokoło 15 – 16 cyfr

Przykład zapisu liczby 43,25

Rozważmy liczbę dziesiętną

\[43{,}25_{10}.\]

Jej część całkowita ma w systemie binarnym postać

\[43_{10}=101011_2,\]

natomiast część ułamkowa

\[0{,}25_{10}=0{,}01_2.\]

Otrzymujemy zatem

\[43{,}25_{10}=101011{,}01_2.\]

Po normalizacji:

\[101011{,}01_2=1{,}0101101_2\cdot 2^5.\]

Liczba jest dodatnia, dlatego bit znaku wynosi

\[s=0.\]

Rzeczywisty wykładnik jest równy \(5\). W formacie pojedynczej precyzji trzeba dodać do niego przesunięcie 127:

\[E=5+127=132.\]

Liczba \(132\) ma zapis binarny

\[132_{10}=10000100_2.\]

W polu mantysy umieszcza się bity znajdujące się po pierwszej jedynce znormalizowanego zapisu:

\[01011010000000000000000.\]

Pełny 32-bitowy zapis ma więc postać:

\[\underbrace{0}_{\text{znak}}\ \underbrace{10000100}_{\text{cecha}}\ \underbrace{01011010000000000000000}_{\text{mantysa}}.\]

Infografika pokazująca okresowe rozwinięcia binarne liczb 0,1 i 0,2, obcięcie części bitów mantysy oraz wynik dodawania różniący się nieznacznie od 0,3.
Niektóre ułamki dziesiętne mają nieskończone rozwinięcia binarne, dlatego komputer przechowuje jedynie ich skończone przybliżenia.

Dlaczego nie wszystkie liczby można zapisać dokładnie?

W systemie dziesiętnym niektóre ułamki mają skończone rozwinięcie, a inne rozwinięcie nieskończone. Przykładowo:

\[\frac{1}{4}=0{,}25,\]

natomiast

\[\frac{1}{3}=0{,}333333\ldots\]

W systemie binarnym ułamek ma skończone rozwinięcie tylko wtedy, gdy po skróceniu jego mianownik jest potęgą liczby \(2\). Dlatego liczby takie jak \(0{,}5\), \(0{,}25\) i \(0{,}125\) można zapisać dokładnie:

\[0{,}5_{10}=0{,}1_2,\]

\[0{,}25_{10}=0{,}01_2,\]

\[0{,}125_{10}=0{,}001_2.\]

Liczby \(0{,}1\) i \(0{,}2\) mają natomiast nieskończone okresowe rozwinięcia binarne:

\[0{,}1_{10}=0{,}00011001100110011\ldots{}_2,\]

\[0{,}2_{10}=0{,}0011001100110011\ldots{}_2.\]

Komputer dysponuje skończoną liczbą bitów, dlatego musi obciąć lub zaokrąglić nieskończone rozwinięcie. W pamięci zostaje zapisana liczba bardzo bliska wartości właściwej, lecz nie zawsze dokładnie jej równa.

Dlaczego \(0{,}1+0{,}2\) nie zawsze daje dokładnie \(0{,}3\)?

Zarówno \(0{,}1\), jak i \(0{,}2\) są przechowywane w komputerze jako przybliżenia. Po wykonaniu dodawania powstaje kolejna liczba przybliżona. W podwójnej precyzji jej dokładna wartość może zostać wyświetlona jako

\[0{,}30000000000000004.\]

Różnica względem \(0{,}3\) jest niezwykle mała, ale może być widoczna po wyświetleniu odpowiednio dużej liczby cyfr. Nie oznacza to uszkodzenia procesora ani błędu języka programowania. Jest to naturalna konsekwencja binarnej reprezentacji liczb.

Schemat konwersji liczby 43,25 na system binarny, normalizacji zapisu oraz wyznaczenia bitu znaku, cechy i mantysy w formacie IEEE 754.
Zapis liczby w formacie IEEE 754 wymaga konwersji na system binarny, normalizacji oraz wyznaczenia bitu znaku, cechy i mantysy.

Zaokrąglanie liczb zmiennoprzecinkowych

Gdy dokładny wynik działania nie może zostać zapisany w wybranym formacie, musi zostać zastąpiony jedną z sąsiednich liczb reprezentowalnych. Standard IEEE 754 definiuje kilka sposobów zaokrąglania.

Domyślnie stosuje się zazwyczaj zaokrąglanie do najbliższej wartości z regułą „do parzystej” w przypadku wyniku leżącego dokładnie w połowie między dwiema liczbami. Ogranicza to systematyczne przesuwanie wyników w jednym kierunku podczas długich serii obliczeń.

Epsilon maszynowy

Epsilon maszynowy określa odległość między liczbą \(1\) a następną większą liczbą możliwą do zapisania w danym formacie zmiennoprzecinkowym.

Można go opisać warunkiem

\[1+\varepsilon_{\mathrm{mach}}>1,\]

przy czym dla odpowiednio mniejszych liczb dodanie ich do jedynki może nie zmienić zapisanego wyniku:

\[1+\frac{\varepsilon_{\mathrm{mach}}}{2}=1.\]

Dla pojedynczej precyzji:

\[\varepsilon_{\mathrm{mach}}=2^{-23}\approx 1{,}1920929\cdot 10^{-7}.\]

Dla podwójnej precyzji:

\[\varepsilon_{\mathrm{mach}}=2^{-52}\approx 2{,}220446049250313\cdot 10^{-16}.\]

W niektórych podręcznikach przez epsilon maszynowy rozumie się maksymalny względny błąd pojedynczego zaokrąglenia. Wówczas stosowana wartość jest dwukrotnie mniejsza i bywa nazywana zaokrągleniem jednostkowym:

\[u=\frac{\varepsilon_{\mathrm{mach}}}{2}.\]

Epsilon maszynowy nie oznacza najmniejszej dodatniej liczby, jaką można zapisać. Opisuje on rozdzielczość liczb znajdujących się w pobliżu jedynki. Odległość między sąsiednimi wartościami rośnie wraz ze wzrostem rzędu wielkości liczby.

Rozmieszczenie liczb zmiennoprzecinkowych

Liczby zmiennoprzecinkowe nie są rozmieszczone na osi liczbowej równomiernie. W pobliżu zera leżą bardzo gęsto, natomiast dla dużych wartości odległość między kolejnymi liczbami rośnie.

W określonym przedziale wykładnika odstęp między sąsiednimi wartościami jest stały. Po przejściu do kolejnej potęgi liczby \(2\) odstęp ten podwaja się.

Oznacza to, że duża liczba może nie zmienić się po dodaniu do niej stosunkowo małej wartości. Przykładowo w ograniczonej precyzji może wystąpić sytuacja

\[x+1=x,\]

jeżeli odległość między sąsiednimi liczbami reprezentowalnymi w pobliżu \(x\) jest większa od \(1\).

Liczby znormalizowane i zdenormalizowane

Większość liczb w formacie IEEE 754 jest zapisywana w postaci znormalizowanej, z ukrytą jedynką przed przecinkiem binarnym:

\[x=(-1)^s\cdot (1{,}f)_2\cdot 2^{E-\operatorname{bias}}.\]

Dla bardzo małych wartości wykładnik nie może być już dalej zmniejszany. Stosuje się wtedy liczby zdenormalizowane, nazywane również subnormalnymi.

Dla liczby subnormalnej ukryta jedynka zostaje zastąpiona zerem:

\[x=(-1)^s\cdot (0{,}f)_2\cdot 2^{1-\operatorname{bias}}.\]

Liczby subnormalne umożliwiają łagodne przejście między najmniejszymi liczbami znormalizowanymi a zerem. Precyzja względna stopniowo maleje, ale bardzo mały wynik nie zostaje natychmiast zastąpiony zerem.

Wartości specjalne

Nie wszystkie kombinacje bitów w standardzie IEEE 754 oznaczają zwykłe liczby skończone. Część z nich przeznaczono do reprezentowania zera, nieskończoności, liczb subnormalnych i wartości nieokreślonych.

Pole cechyMantysaZnaczenie
same zerasame zera\(+0\) albo \(-0\)
same zeraróżna od zeraliczba subnormalna
wartość pośredniadowolnaliczba znormalizowana
same jedynkisame zera\(+\infty\) albo \(-\infty\)
same jedynkiróżna od zeraNaN

Dodatnie i ujemne zero

Standard rozróżnia \(+0\) i \(-0\). W większości porównań wartości te są sobie równe, ale znak zera może mieć znaczenie w niektórych działaniach, na przykład:

\[\frac{1}{+0}=+\infty,\qquad \frac{1}{-0}=-\infty.\]

Nieskończoność

Nieskończoność może powstać między innymi po przekroczeniu największej reprezentowalnej wartości albo podczas dzielenia liczby niezerowej przez zero.

Przykładowo:

\[\frac{1}{0}=+\infty,\]

o ile środowisko obliczeniowe stosuje reguły IEEE 754 i nie przerywa działania wyjątkiem.

Wartość NaN

Skrót NaN pochodzi od angielskiego określenia Not a Number. Wartość ta oznacza wynik operacji, której nie można przypisać zwykłej wartości liczbowej.

NaN może powstać na przykład podczas obliczania:

Wartość NaN ma nietypowe własności porównawcze. Nie jest równa żadnej liczbie, a nawet samej sobie:

\[\operatorname{NaN}\ne\operatorname{NaN}.\]

Nadmiar i niedomiar

Nadmiar

Nadmiar, nazywany po angielsku overflow, występuje wtedy, gdy wartość wyniku przekracza zakres danego formatu.

W zależności od sposobu wykonywania obliczeń wynikiem może być nieskończoność, największa liczba skończona albo zgłoszenie błędu.

Niedomiar

Niedomiar, czyli underflow, występuje wtedy, gdy dodatnia wartość jest zbyt mała, aby można ją było przedstawić jako liczbę znormalizowaną.

Początkowo wynik może zostać zapisany jako liczba subnormalna. Po dalszym zmniejszaniu zostaje zaokrąglony do zera.

Najczęstsze problemy w obliczeniach

Działania nie zawsze są łączne

W matematyce rzeczywistej dodawanie jest działaniem łącznym:

\[(a+b)+c=a+(b+c).\]

W arytmetyce zmiennoprzecinkowej równość ta nie musi być spełniona, ponieważ po każdym działaniu następuje zaokrąglenie.

Jeżeli \(a\) jest bardzo duże, a \(b\) i \(c\) znacznie mniejsze, może wystąpić sytuacja:

\[(a+b)+c\ne a+(b+c).\]

Kolejność sumowania może więc wpływać na wynik. Przy dodawaniu wielu liczb często korzystniej jest rozpoczynać od wartości o najmniejszym module.

Utrata cyfr znaczących

Szczególnie niebezpieczne jest odejmowanie dwóch prawie równych liczb. Ich początkowe cyfry znaczące redukują się, a w wyniku pozostają głównie cyfry obarczone błędem zaokrąglenia.

Zjawisko to nazywa się katastrofalnym odejmowaniem lub utratą cyfr znaczących.

Przykładowo wyrażenie

\[\sqrt{x+1}-\sqrt{x}\]

dla dużego \(x\) prowadzi do odejmowania dwóch bardzo podobnych liczb. Korzystniejsza numerycznie jest postać uzyskana przez usunięcie niewymierności z licznika:

\[\sqrt{x+1}-\sqrt{x}=\frac{1}{\sqrt{x+1}+\sqrt{x}}.\]

Oba wzory są matematycznie równoważne, lecz drugi jest zwykle stabilniejszy numerycznie.

Porównywanie liczb

Wynik obliczeń zmiennoprzecinkowych nie zawsze powinien być porównywany z wartością oczekiwaną za pomocą dokładnej równości.

Zamiast sprawdzać:

\[x=y,\]

często sprawdza się, czy różnica jest dostatecznie mała:

\[|x-y|\leq \varepsilon.\]

Dla liczb o bardzo różnym rzędzie wielkości lepiej stosować jednocześnie tolerancję bezwzględną i względną, na przykład:

\[|x-y|\leq \varepsilon_{\mathrm{abs}}+\varepsilon_{\mathrm{rel}}\max(|x|,|y|).\]

Wartość tolerancji należy dobrać do skali danych, dokładności pomiarów i rodzaju wykonywanych obliczeń. Nie powinna być ona automatycznie utożsamiana z epsilonem maszynowym.

Sumowanie wielu składników

Podczas sumowania bardzo dużej liczby wartości niewielkie błędy mogą się kumulować. Znaczenie ma również kolejność składników.

Dokładność można poprawić między innymi przez:

Błąd bezwzględny i względny

Jeżeli dokładna wartość wynosi \(x\), a wartość zapisana lub obliczona przez komputer jest równa \(\operatorname{fl}(x)\), błąd bezwzględny wynosi

\[\Delta x=|x-\operatorname{fl}(x)|.\]

Błąd względny określa stosunek błędu bezwzględnego do modułu wartości dokładnej:

\[\delta x=\frac{|x-\operatorname{fl}(x)|}{|x|}.\]

Błąd względny pozwala ocenić dokładność niezależnie od skali liczby. Nie można go jednak bezpośrednio stosować dla \(x=0\), ponieważ prowadziłoby to do dzielenia przez zero.

Arytmetyka zmiennoprzecinkowa a metody numeryczne

W metodach numerycznych wynik zależy nie tylko od błędu wynikającego z przybliżonej metody, lecz również od błędów powstających podczas wykonywania działań zmiennoprzecinkowych.

Można wyróżnić między innymi:

Zmniejszenie kroku obliczeniowego nie zawsze poprawia wynik bez ograniczeń. Mniejszy krok może zmniejszyć błąd metody, ale jednocześnie zwiększyć liczbę działań i udział błędów zaokrągleń.

W praktyce często istnieje optymalny zakres wartości kroku, w którym łączny błąd jest najmniejszy.

Zjawiska te mają znaczenie między innymi przy różniczkowaniu numerycznym, całkowaniu, rozwiązywaniu równań nieliniowych oraz obliczeniach iteracyjnych. Dotyczą także dopasowywania funkcji i przetwarzania danych, takich jak zagadnienia omówione w artykule Aproksymacja i interpolacja.

Jak ograniczać błędy obliczeń?

Większa liczba wykonanych działań nie musi oznaczać większej dokładności. Każde działanie zmiennoprzecinkowe może wprowadzać kolejny błąd zaokrąglenia.

Podsumowanie

Arytmetyka zmiennoprzecinkowa umożliwia zapisywanie liczb o bardzo różnym rzędzie wielkości za pomocą ograniczonej liczby bitów. Liczba jest reprezentowana przez znak, cechę i część znaczącą, tradycyjnie nazywaną mantysą.

Standard IEEE 754 definiuje formaty pojedynczej i podwójnej precyzji, zasady zaokrąglania oraz wartości specjalne, takie jak zero ze znakiem, liczby subnormalne, nieskończoność i NaN.

Nie wszystkie ułamki dziesiętne można dokładnie przedstawić w systemie binarnym. Liczby takie jak \(0{,}1\) i \(0{,}2\) są zapisywane jako skończone przybliżenia, co może prowadzić do niewielkich różnic w wynikach obliczeń.

Ważnym parametrem formatu jest epsilon maszynowy, określający odstęp między jedynką a następną większą liczbą reprezentowalną. Nie należy go jednak mylić z najmniejszą dodatnią liczbą ani automatycznie stosować jako tolerancji we wszystkich porównaniach.

Świadomość ograniczeń arytmetyki zmiennoprzecinkowej pozwala dobierać stabilniejsze algorytmy, właściwie interpretować wyniki i unikać wielu trudnych do wykrycia błędów obliczeniowych.

Powiązane artykuły

Masz problem z tym tematem?

Wszechwiedza.pl pomaga zrozumieć matematykę, statystykę, ekonometrię, badania operacyjne, analizę danych, mechanikę, rachunkowość i wiele innych przedmiotów — spokojnie, konkretnie i krok po kroku. 

Zapytaj o pomoc