Liczba zmiennoprzecinkowa

komputerowa reprezentacja liczby rzeczywistej zapisanej za pomocą notacji naukowej

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

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

 

gdzie:

  (ang. sign) – znak liczby, 1 lub −1,
  (ang. mantissa) – znormalizowana mantysa, liczba ułamkowa[1],
  (ang. base) – podstawa systemu liczbowego[1] (2 dla systemów komputerowych),
  (ang. exponent) – wykładnik, cecha, liczba całkowita[1].

Mantysa jest znormalizowana, tj. należy do przedziału   (przedział prawostronnie otwarty!). Jeżeli   jest stałe, a   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[2].

Załóżmy, że   to liczba cyfr przeznaczonych na mantysę, natomiast   to liczba cyfr przeznaczonych na wykładnik (  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   i   określone są następująco:

  • Wykładnik  
    •  
    •  
  • Mantysa  
    •  
    •  

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

  •  
  •  

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

 

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

Błąd względny reprezentacji wynosi   (inaczej: połowa wagi najmniej znaczącej cyfry mantysy). Błędów bezwzględnych na ogół się nie podaje.

Jeżeli komputer wygeneruje liczbę   to traktowana jest jako niedomiar (underflow).

W przypadku, gdy otrzymana liczba   to traktowana jest jako nadmiar wykładniczy (overflow).

Przykład reprezentacji

edytuj

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

Liczba   odpowiada  

Normalizacja mantysy:

Mantysa nie należy do przedziału   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.
 

Odcięcie i zaokrąglenie mantysy:

po odcięciu:  
po zaokrągleniu:  

Wynik:

 

Przykład dla liczby mniejszej od 1:  

 

Po normalizacji:

 

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

Wynik:

 

Operacje na liczbach zmiennoprzecinkowych

edytuj

Własności arytmetyki zmiennoprzecinkowej

edytuj

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

  •  
  •  

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

  •  

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

Załóżmy, że chcemy dodać lub odjąć dwie dodatnie liczby zmiennoprzecinkowe:   oraz   przy czym   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:

 

Wówczas:

 

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   przez czynnik   W szczególnym przypadku, jeśli   jest większe niż   (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   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

Mając dane liczby zmiennoprzecinkowe   i  

 
 

Błędy operacji elementarnych

edytuj

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

 

Wówczas błąd względny poszczególnych operacji elementarnych wykonywanych na liczbach   oraz   można oszacować następująco:

  • dodawanie/odejmowanie:  
  • mnożenie:  
  • dzielenie:  

gdzie     i   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
 
Reprezentacja zmiennoprzecinkowa IEEE-754 single
Zobacz też: IEEE 754.

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   jest więc postaci 1.xxxxx.... (gdzie x – bit o dowolnej wartości). Ponieważ część całkowita jest znana i równa zawsze 1, nie jest ona zapamiętywana, co daje dodatkowy bit na część ułamkową.
  2. Ponieważ znak liczby jest zapamiętywany na jednym bicie, otrzymanie modułu lub 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, single precision),
  • podwójnej precyzji (ang. double, double precision).

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 sprzętowo liczby 10-bajtowe, natomiast kompilator Pascala – w sposób programowy liczby 6-bajtowe[a]. 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 ponadto ułatwiają przenoszenie kodu programu na inną platformę sprzętową.

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[3]
SSE5, OpenGL 3.0[4] 1 5 10 16
BFloat16[5] 1 8 7 16 Brain Floating Point stosowany w uczeniu maszynowym i sieciach neuronowych, __bf16 w niektórych kompilatorach C i C++[6][7]

Przesunięcie wykładnika

edytuj

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:

 

gdzie   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

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; w większości przypadków wykonanie działania 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). Wszystkie bity wykładnika są równe 0 i
 
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

C, C++

edytuj

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

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[3]
  • real, 6 B, 11-12 cyfr, 2.9·10−39..1.7·1038[3]
  • double, 8 B, 15-16 cyfr, 5.0·10−324..1.7·10308[3]
  • extended, 10 B, 19-20 cyfr, 3.4·10−4932..1.1·104932[8]

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

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[9].

Kalkulator

edytuj

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

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

Zobacz też

edytuj
  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.

Przypisy

edytuj
  1. a b c Fortuna, Macukow i Wąsowski 1993 ↓, s. 10.
  2. Jankowski 1990 ↓, s. 92.
  3. a b c d Marciniak 1991 ↓, s. 539.
  4. Specyfikacja OpenGL 3.0, wersja z 11.08.2008, sekcja 2.1.2 „16-Bit Floating Point Number”.
  5. Dhiraj Kalamkar i inni, A Study of BFLOAT16 for Deep Learning Training, „ArXiv”, arXiv:1905.12322.
  6. BFloat16 floating-point number format [online], keil.com [dostęp 2020-07-21] (ang.).
  7. BFloat16 floating-point number format [online], developer.arm.com [dostęp 2020-07-21] (ang.).
  8. Marciniak 1991 ↓, s. 539–540.
  9. Michael Metcalf, John Ker Reid, Malcolm Cohen: Fortran 95/2003 explained. Oxford: Oxford University Press, 2004, s. 16. ISBN 978-0-19-852693-3.

Bibliografia

edytuj