Liczba zmiennoprzecinkowa

Z Wikipedii, wolnej encyklopedii
Skocz do: nawigacja, szukaj

Liczba zmiennoprzecinkowa – reprezentacja liczby rzeczywistej zapisanej za pomocą notacji naukowej. Ze względu na wygodę operowania na takich liczbach, przyjmuje się ograniczony zakres na mantysę i cechę – nazwy te mają w matematyce znaczenie podane w artykule podłoga i sufit, a w niniejszym artykule inne, powszechne w informatyce. Powoduje to, że reprezentacja liczby rzeczywistej jest tylko przybliżona, a jedna liczba zmiennoprzecinkowa może reprezentować różne liczby rzeczywiste z pewnego zakresu.

Podstawa matematyczna[edytuj | edytuj kod]

Wartość liczby zmiennoprzecinkowej jest obliczana według wzoru:

x = S \cdot M \cdot B^E

gdzie:

Mantysa jest znormalizowana, tj. należy do przedziału [1, B) (przedział prawostronnie otwarty!). Jeżeli M jest stałe, a E zmienia się, wówczas przesunięciu ulega przecinek – stąd właśnie pochodzi nazwa tej reprezentacji.

Zarówno dla mantysy jak i wykładnika liczba cyfr jest z góry ustalona. Zatem dana liczba jest reprezentowana z pewną skończoną dokładnością i należy do skończonego zbioru wartości.

Załóżmy, że m to liczba cyfr przeznaczonych na mantysę, natomiast n+1 to liczba cyfr przeznaczonych na wykładnik (n cyfr dla wartości i 1 dla znaku wykładnika). Uznaje się również, że jedna dodatkowa pozycja (najstarsza) zarezerwowana jest dla zapisu znaku całej liczby. Wówczas wartości maksymalne i minimalne dla M i E określone są następująco:

  • Wykładnik E:
    • E_{\min} = -B^{n}+1
    • E_{\max} = B^{n}-1
  • Mantysa M:
    • M_{\min} = 1
    • M_{\max} = B-B^{-(m-1)}

Stąd najmniejsza i największa liczba dodatnia możliwa do zapisania w takiej reprezentacji to:

  • x_{\min} = M_{\min} \cdot B^{E_{\min}} = 1 \cdot B^{E_{min}}
  • x_{\max} = M_{\max} \cdot B^{E_{\max}} = \left( B-B^{-(m-1)} \right) \cdot B^{E_{max}} < B^{E_{max}+1}.

Zakres liczb, które mogą być reprezentowane w danym zapisie wynosi:

[-x_{\max}, -x_{\min}] \cup \{0\} \cup [x_{\min}, x_{\max}]

Zero jest wartością specjalną, która nie może zostać bezpośrednio reprezentowana w tym formacie.

Błąd względny reprezentacji wynosi \frac{1}{B^{m-1}} (inaczej: waga najmniej znaczącej cyfry mantysy). Błędów bezwzględnych na ogół się nie podaje.

Jeżeli komputer wygeneruje liczbę |x|<B^{E_{\min}}, to traktowana jest jako niedomiar (underflow).

W przypadku, gdy otrzymana liczba |x|>M_{\max} \cdot B^{E_{\max}}, to traktowana jest jako nadmiar wykładniczy (overflow).

Przykład reprezentacji[edytuj | edytuj kod]

Przyjmijmy, że B=10, liczba cyfr dziesiętnych przeznaczonych na mantysę wynosi 4, natomiast na wykładnik 2. Chcemy zapisać wartość 60,89523.

Liczba 60,89523 odpowiada M=60,89523, E=0.

Normalizacja mantysy.

Mantysa nie należy do przedziału [1,10), zatem należy przesuwać przecinek w lewo aż będzie doń należała. Przesuwanie przecinka w lewo wiąże się ze zwiększaniem wykładnika.
M=6,089523, E=1.

Odcięcie i zaokrąglenie mantysy.

po odcięciu: 6,089,
po zaokrągleniu: 6,090.

Wynik:

6,090•101 = 6,090 E 1

Przykład dla liczby mniejszej od 1: 0,0000125.

M= 0,0000125, E = 0.

Po normalizacji

M = 1,25, E = -5.

Liczba cyfr znaczących jest mniejsza od dostępnej, więc nie jest potrzebne zaokrąglanie.

Wynik:

1,250•10-5 = 1,25 E -5

Operacje na liczbach zmiennoprzecinkowych[edytuj | edytuj kod]

Własności arytmetyki zmiennoprzecinkowej[edytuj | edytuj kod]

Arytmetyka zmiennoprzecinkowa nie jest łączna. To znaczy, że dla x, y i z mogą zachodzić różności:

  •  (x + y) + z \neq x + (y + z)
  •  (x \cdot y) \cdot z \neq x \cdot (y \cdot z) ,

Nie jest też rozdzielna, czyli może zachodzić różność:

  •  x \cdot (y + z) \neq (x \cdot y) + (x \cdot z)

Innymi słowy, kolejność wykonywania operacji wpływa na końcowy wynik.

Przy obliczeniach zmiennoprzecinkowych występują też:

  • zaokrąglenia
  • nieprawidłowe operacje
  • przepełnienie
  • niedomiar

Dodawanie i odejmowanie[edytuj | edytuj kod]

Załóżmy że chcemy dodać lub odjąć dwie dodatnie liczby zmiennoprzecinkowe: x_1 = M_1 \cdot B^{E_1} oraz x_2 = M_2 \cdot B^{E_2}, przy czym x_1 \ge x_2. Założenia te można spełnić dla dowolnych liczb zmiennoprzecinkowych manipulując ich kolejnością, znakiem wyniku oraz rodzajem wykonywanej operacji, według poniższego schematu:

\begin{matrix}
(+x_1)+(+x_2), & (+x_1)-(-x_2), & (+x_2)+(+x_1), & (+x_2)-(-x_1) & \rightarrow & +(x_1+x_2) \\
(+x_1)+(-x_2), & (+x_1)-(+x_2), & (-x_2)+(+x_1), & (-x_2)-(-x_1) & \rightarrow & +(x_1-x_2) \\
(-x_1)+(-x_2), & (-x_1)-(+x_2), & (-x_2)+(-x_1), & (-x_2)-(+x_1) & \rightarrow & -(x_1+x_2) \\
(-x_1)+(+x_2), & (-x_1)-(-x_2), & (+x_2)+(-x_1), & (+x_2)-(+x_1) & \rightarrow & -(x_1-x_2) \\
\end{matrix}

Wówczas:

x_1 \pm x_2 = (M_1 \pm M_2 \cdot B^{E_2-E_1}) \cdot B^{E_1}

Jeśli liczby mają różne wykładniki, to podczas dodawania mantysa liczby o mniejszym wykładniku musi zostać zdenormalizowana – we wzorze jest to przemnożenie M_2 przez czynnik B^{E_2-E_1}. W szczególnym przypadku, jeśli E_1-E_2 jest większe niż m (liczba cyfr mantysy), to po denormalizacji mantysa będzie miała wartość 0, a liczba o mniejszym wykładniku nie wpłynie na wynik dodawania bądź odejmowania.

Odejmowanie liczb zmiennoprzecinkowych o takim samym wykładniku E i niewiele różniącej się mantysie powoduje, że wynikowa mantysa jest znacznie zdenormalizowana. Renormalizacja w takim wypadku wiąże się z wprowadzeniem sporej liczby nieznaczących zer na końcu mantysy. Jest to szczególnie niekorzystne, dlatego zwykle tak projektuje się obliczenia, by do tego nie dopuścić.

Mnożenie i dzielenie[edytuj | edytuj kod]

Mając dane liczby zmiennoprzecinkowe x_1 = S_1 \cdot M_1 \cdot B^{E_1} i x_2 = S_2 \cdot M_2 \cdot B^{E_2}:

x_1 \cdot x_2 = (S_1 \cdot S_2) \cdot (M_1 \cdot M_2) \cdot B^{E_1+E_2}
x_1/x_2 = (S_1 \cdot S_2) \cdot (M_1 / M_2) \cdot B^{E_1-E_2}

Błędy operacji elementarnych[edytuj | edytuj kod]

Wygodnie jest przedstawić liczbę zmiennoprzecinkową jako wartość dokładną zaburzoną pewnym błędem reprezentacji \varepsilon:

\bar{x} = x(1 + \varepsilon_x)

Wówczas błąd względny poszczególnych operacji elementarnych wykonywanych na liczbach \bar{a}=a(1+\varepsilon_a) oraz \bar{b}=b(1+\varepsilon_b) można oszacować następująco:

  • dodawanie/odejmowanie: \varepsilon_{a \pm b} = \frac{a \varepsilon_a \pm b \varepsilon_b}{a \pm b} + \varepsilon_{\pm}
  • mnożenie: \varepsilon_{a \cdot b} = \varepsilon_a + \varepsilon_b + \varepsilon_{\cdot}
  • dzielenie: \varepsilon_{a / b} = \varepsilon_a - \varepsilon_b + \varepsilon_{/}

gdzie \varepsilon_{\pm}, \varepsilon_{\cdot} i \varepsilon_{/} to błędy wprowadzane przez dane operacje arytmetyczne.

Rozbijając każde wyrażenie arytmetyczne na operacje elementarne można za pomocą tych zależności oszacować powstałe błędy. Istnieją jednak lepsze i szybsze metody modelowania błędów.

Implementacje sprzętowe[edytuj | edytuj kod]

Reprezentacja zmiennoprzecinkowa IEEE-754 single

W implementacjach sprzętowych liczby zmiennoprzecinkowe wyraża się liczbami dwójkowymi (B=2). Ma to następujące zalety:

  1. Mantysa należy do przedziału [1,2), jest więc postaci 1.xxxxx.... (x – bit o dowolnej wartości). Ponieważ część całkowita jest znana, i równa zawsze 1, przeto nie jest zapamiętywana, co daje dodatkowy bit na część ułamkową.
  2. Ponieważ znak liczby jest zapamiętywany na jednym bicie, przeto otrzymanie modułu i wartości przeciwnej wymaga, odpowiednio, wyzerowania tego bitu (logiczna operacja AND), lub zmiany na wartość przeciwną (logiczna operacja XOR).

W celu ujednolicenia zasad operacji na liczbach zmiennoprzecinkowych na różnych platformach sprzętowych, opracowano standard IEEE 754, w oparciu o który realizuje się obecnie wszystkie implementacje sprzętowe liczb zmiennoprzecinkowych. Definiuje on dwie klasy liczb:

  • pojedynczej precyzji (ang. single)
  • podwójnej precyzji (ang. double)

Są również inne sposoby zapisu, różniące się jedynie liczbą bitów przeznaczoną na poszczególne pola. Np. koprocesor w procesorach x86, oprócz typów standardowych, wspiera liczby 10-bajtowe, natomiast kompilator Pascala - liczby 6-bajtowe[1]. Liczby zgodne ze standardem IEEE 754 mają dokładnie określoną semantykę, jak na przykład: dokładność operacji elementarnych, kierunki zaokrągleń, czy obsługa sytuacji wyjątkowych – są to cechy bardzo pożądane w zastosowaniach naukowych i inżynieryjnych, a również ułatwiają przenoszenie kodu programu na inny sprzęt.

Format Znak [bity] Wykładnik [bity] Mantysa [bity] Szerokość słowa [bity] Typy w językach programowania
IEEE-754 single 1 8 23 32 float (C), single (Pascal), real*4 (Fortran)
IEEE-754 double 1 11 52 64 double (C), real lub double (Pascal), real*8 (Fortran)
koprocesor x87 1 15 64 80 long double (C99), extended (Pascal)
Turbo Pascal 1 8 39 48 real
SSE5, OpenGL 3.0[2] 1 5 10 16 w OpenGL nazywana half-float

Przesunięcie wykładnika[edytuj | edytuj kod]

Wykładnik będący liczbą całkowitą jest zapisywany w kodzie spolaryzowanym, co można interpretować jako wartość przesuniętą o pewną stałą (ang. biased exponent). Właściwą wartość wykładnika uzyskuje się odejmując od zakodowanego wykładnika wartość przesunięcia (ang. bias). Wartość liczby zmiennoprzecinkowej oblicza się ze wzoru:

x = (-1)^S \cdot M \cdot 2^{E - \textrm{bias}}

gdzie S to bit znaku; liczba jest ujemna, gdy bit znaku jest równy 1, w przeciwnej sytuacji ma on wartość 0.

Typowe wartości przesunięcia dla koprocesora x87 (występującego w procesorach x86) wynoszą:

  • 127 (7FH) w formacie 32-bitowym
  • 1023 (3FFH) w formacie 64-bitowym
  • 16383 (3FFFH) w formacie 80-bitowym

Wartości specjalne[edytuj | edytuj kod]

Oprócz zwykłych liczb zdefiniowano następujące wartości specjalne:

  • NaN – nie-liczba (ang. Not-a-Number), to symbol, który nie reprezentuje wartości liczbowej, powstały zazwyczaj w wyniku niedozwolonej operacji (np. pierwiastkowanie liczby ujemnej)
    • sNaN – sygnalizujące NaN (ang. signalling NaN) – rozróżnienie wprowadzone w procesorach z rodziny x86; dla większości operacji wykonanie liczba operacja sNaN spowoduje zgłoszenie wyjątku.
    • qNaN – ciche NaN (ang. quiet NaN) – przekazanie tej wartości jako argumentu operacji nie powoduje zgłoszenia wyjątku; w operacjach SSE można ustalić, że liczba operacja qNaN → 0.
  • Zero – rozróżnia się +0,0 i -0,0.
  • Nieskończoność – jest wynikiem operacji w przypadku wystąpienia nadmiaru (przepełnienia), przy dzieleniu przez 0, itp.; może być dodatnia lub ujemna.
  • Liczba nieznormalizowana – pojawia się, gdy występuje niedomiar (ang. underflow), ale wynik operacji jeszcze można zapisać denormalizując mantysę (w takim przypadku mantysa reprezentuje liczbę w postaci 0,xxx...xxx, a nie 1,xxx...xxxx).
Wartość specjalna Bit znaku Bity wykładnika Bity mantysy Uwagi
NaN x 111..111 xxxx...xxx wszystkie bity wykładnika są równe 1, natomiast mantysa ma niezerową wartość
QNaN x 111..111 1xxx...xxx uwagi jak dla NaN, ale pierwszy bit mantysy zawsze równy 1
SNaN x 111..111 0xxx...xxx uwagi jak dla NaN, ale pierwszy bit mantysy zawsze równy 0
±Zero x 000..000 0000...000 wszystkie bity mantysy i wykładnika równe 0, bit znaku decyduje o znaku wartości
±Nieskończoność x 111..111 0000...000 wszystkie bity mantysy równe 0, wszystkie bity wykładnika równe 1, bit znaku decyduje o znaku wartości
Nieznormalizowana x 000..000 xxxx...xxx mantysa różna od 0, wszystkie bity wykładnika równe 0, bit znaku decyduje o znaku wartości

Typy zmiennoprzecinkowe w językach programowania[edytuj | edytuj kod]

C, C++[edytuj | edytuj kod]

Rozmiar typów zmiennoprzecinkowych zależy od konkretnych implementacji. Standardowo, typ float zajmuje co najmniej 4 bajty, double 8 bajtów, a long double zazwyczaj 8-16 bajtów. W przypadku kompilatora GCC w wersji 4.6.2, długości typów wynoszą odpowiednio 4, 8 i 16 bajtów, a w Visual C++ – 4, 8 i 8 bajtów.

Pascal[edytuj | edytuj kod]

W standardzie języka Pascal ISO/IEC 7185 :1990 jest wymagany typ real obejmujący podzbiór liczb rzeczywistych. Turbo Pascal wykorzystuje cztery typy zmiennoprzecinkowe (typ, liczba bajtów, liczba cyfr znaczących, zakres wartości):

  • single, 4 B, 7-8 cyfr, 1.5·10-45..3.4·1038
  • real, 6 B, 11-12 cyfr, 2.9·10-39..1.7·1038
  • double, 8 B, 15-16 cyfr, 5.0·10-324..1.7·10308
  • extended, 10 B, 19-20 cyfr, 3.4·10-4932..1.1·104932

Delphi obsługuje te same typy, przy czym w domyślnych ustawieniach opcji kompilatora typ real jest równoważny typowi double, a sześciobajtowy nazwano real48. We free Pascalu typ real jest zastępowany przez single lub double.

Fortran[edytuj | edytuj kod]

W oryginalnej specyfikacji języka Fortran typ real miał dwie możliwe długości REAL i DOUBLE PRECISION. Zakres i precyzja obydwu typów nie były wyspecyfikowane, lecz zależały od architektury konkretnego komputera, z wymaganiem aby DOUBLE PRECISION miał wyższą precyzję i co najmniej taki sam zakres co REAL.

Z czasem rynek został całkowicie zdominowany przez komputery o architekturze opartej na 8-bitowych bajtach, przyjęło się, że podstawowy typ REAL zajmuje 4 bajty. Współczesne kompilatory Fortranu dopuszczają deklaracje:

  • REAL*4 lub po prostu REAL – 32 bity, odpowiednik typu float w języku C
  • REAL*8 lub DOUBLE PRECISION – 64 bity, odpowiednik typu double w języku C

Niektóre implementacje dopuszczają także typ:

  • REAL*16 – 128 bitów.

Nowsze specyfikacje języka Fortran, poczynając od Fortran90, umożliwiają programiście deklarowanie wymaganej precyzji i zakresu liczb zmiennoprzecinkowych w oderwaniu od konkretnej implementacji. Wbudowana funkcja SELECTED_REAL_KIND(p,r) zwraca najkrótszą, dla konkretnego procesora, reprezentację o co najmniej wskazanej dokładności i zakresie. Na przykład SELECTED_REAL_KIND(10,80) zwróci dla danego procesora typ liczby zmiennoprzecinkowej o dokładności co najmniej 10 cyfr znaczących i zakresie co najmniej do 1080. Istnieją też wbudowane funkcje PRECISION i RANGE pozwalające sprawdzić jaki rzeczywisty zakres i dokładność mają liczby zmiennoprzecinkowe podanego typu w danym procesorze[3].

Kalkulator[edytuj | edytuj kod]

Sposoby wyświetlania liczb zmiennoprzecinkowych:

  • FLO (Floating Notation) – notacja dziesiętna – tryb domyślny. Jeżeli jest to możliwe wyświetla liczbę z wykładnikiem równym 0 pomijając jego wyświetlanie
  • SCE (Scientific Notation) – notacja naukowa – zawsze wyświetla liczbę z wykładnikiem
  • ENG (Engineering Notation) – notacja inżynierska – zawsze wyświetla liczbę z wykładnikiem podzielnym przez 3.

Historia[edytuj | edytuj kod]

Binarne liczby zmiennoprzecinkowe po raz pierwszy zastosował Konrad Zuse w mechanicznym komputerze Z1.

Zobacz też[edytuj | edytuj kod]

Przypisy

  1. Firma Borland w kompilatorach języka Pascal począwszy od wersji Delphi 3.0 przyjęła za standard liczb zmiennoprzecinkowych liczby typu double, a stary typ został nazwany real48 i jest obsługiwany w celu zachowania zgodności z poprzednimi wersjami, ale ma status przestarzałego elementu języka, który w pewnym momencie może przestać być obsługiwany.
  2. Specyfikacja OpenGL 3.0, wersja z 11.08.2008, sekcja 2.1.2 "16-Bit Floating Point Number"
  3. Michael Metcalf, John Ker Reid, Malcolm Cohen: Fortran 95/2003 explained. Oxford: Oxford University Press, 2004, s. 16. ISBN 978-0-19-852693-3.