Liczba zmiennoprzecinkowa
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:
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 | edytuj kod]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 | edytuj kod]Własności arytmetyki zmiennoprzecinkowej
[edytuj | edytuj kod]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 | edytuj kod]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 | edytuj kod]Mając dane liczby zmiennoprzecinkowe i
Błędy operacji elementarnych
[edytuj | edytuj kod]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 | edytuj kod]W implementacjach sprzętowych liczby zmiennoprzecinkowe wyraża się liczbami dwójkowymi (B = 2). Ma to następujące zalety:
- Mantysa należy do przedziału jest więc postaci
1.xxxxx....
(gdziex
– 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ą. - 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 | 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:
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 | 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; 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
.
- sNaN – sygnalizujące NaN (ang. signalling NaN) – rozróżnienie wprowadzone w procesorach z rodziny x86; w większości przypadków wykonanie działania
- 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 nie1,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 | 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[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 | 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[9].
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]Uwagi
[edytuj | edytuj kod]- ↑ 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 | edytuj kod]- ↑ a b c Fortuna, Macukow i Wąsowski 1993 ↓, s. 10.
- ↑ Jankowski 1990 ↓, s. 92.
- ↑ a b c d Marciniak 1991 ↓, s. 539.
- ↑ Specyfikacja OpenGL 3.0, wersja z 11.08.2008, sekcja 2.1.2 „16-Bit Floating Point Number”.
- ↑ Dhiraj Kalamkar i inni, A Study of BFLOAT16 for Deep Learning Training, „ArXiv”, arXiv:1905.12322 .
- ↑ BFloat16 floating-point number format [online], keil.com [dostęp 2020-07-21] (ang.).
- ↑ BFloat16 floating-point number format [online], developer.arm.com [dostęp 2020-07-21] (ang.).
- ↑ Marciniak 1991 ↓, s. 539–540.
- ↑ 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 | edytuj kod]- Zenon Fortuna , Bohdan Macukow , Janusz Wąsowski , Metody numeryczne, Warszawa: Wydawnictwa Naukowo-Techniczne, 1993, ISBN 83-204-1551-9 .
- Michał Jankowski , Elementy grafiki komputerowej, Warszawa: Wydawnictwa Naukowo-Techniczne, 1990, ISBN 83-204-1326-5 .
- Andrzej Marciniak , Turbo Pascal 5.5, Poznań: NAKOM, 1991 (Biblioteka użytkownika mikrokomputerów; 2), ISBN 83-85060-20-0, ISSN 0867-6011 .