Serwis Edukacyjny w I-LO w Tarnowie ![]() Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2023 mgr Jerzy Wałaszek
|
Aby ujednolicić wyniki obliczeń numerycznych wykonywanych na różnych platformach sprzętowych, wprowadzono ściśle określony standard zapisu zmiennoprzecinkowego IEEE 754. W następnym rozdziale przedstawiamy wywiad z profesorem Williamem Kahanem - jednym z twórców tego standardu. Jak na warunki szkoły średniej standard IEEE 754 jest dosyć skomplikowany, ponieważ nie został on opracowany do nauki lecz do praktycznych zastosowań inżynierskich i naukowych. Jednakże nie widzę powodów, aby zdolny uczeń liceum nie mógł sobie go przyswoić - wiedza ta zaowocuje na studiach wyższych oraz wpłynie na zrozumienie zalet i ograniczeń obliczeń komputerowych. |
Standard IEEE 754 definiuje dwa rodzaje liczb zmiennoprzecinkowych: 32-bitowe (pojedynczej precyzji - ang. single precision) oraz 64-bitowe (podwójnej precyzji - ang. double precision). Kod binarny liczby zmiennoprzecinkowej podzielony jest na trzy pola zawierające komponenty zapisu zmiennoprzecinkowego:
Format zapisu zmiennoprzecinkowego IEEE 754 | |||
---|---|---|---|
32 bity - pojedyncza precyzja | (1 bit) b31 | (8 bitów) b30 ... b23 (BIAS=127) | (23 bity) ,b22 ... b0 (U1) |
64 bity - podwójna precyzja | (1 bit) b63 | (11 bitów) b62 ... b52 (BIAS=1023) | (52 bity) ,b51 ... b0 (U1) |
Opis pół bitowych | bit znaku | bity kodu cechy | bity ułamkowe mantysy |
Pierwszy bit w zapisie liczby zwany jest bitem znaku. Stan 0 oznacza liczbę dodatnią, stan 1 liczbę ujemną. Aby zatem zmienić znak liczby zmiennoprzecinkowej na przeciwny, wystarczy dokonać negacji tego bitu.
Liczby zmiennoprzecinkowe IEEE 754 zapisują cechę w kodzie z nadmiarem.
W pojedynczej precyzji cecha posiada 8 bitów, a nadmiar wynosi 127. Zatem w polu cechy można zapisać wartości od -127 (wszystkie bity b30...b23 wyzerowane) do 128 (wszystkie bity b30...b23 ustawione na 1).
W podwójnej precyzji cecha zbudowana jest z 11 bitów, nadmiar wynosi 1023. Najmniejszą wartością będzie -1023 (bity b62...b52 ustawione na 0) , a największą 1024 (bity b62...b52 ustawione na 1). Wzrost ilości bitów cech liczb zmiennoprzecinkowych wpływa a ich zakres.
UWAGA:Cechy zbudowane z samych zer i z samych jedynek pełnią w standardzie IEEE 754 specjalne funkcje nie związane z ich normalnym znaczeniem. Opisujemy to szczegółowo w dalszej części artykułu. |
W pojedynczej precyzji mantysa posiada 23 bity, a w podwójnej precyzji 52 bity. Wzrost liczby bitów mantys liczb zmiennoprzecinkowych wpływa na ich precyzję, czyli dokładność odwzorowywania liczb rzeczywistych.
Mantysy są zapisywane w stałoprzecinkowym kodzie U1. Ponieważ mantysa jest prawie zawsze znormalizowana (z wyjątkiem wartości zdenormalizowanej, która jest przypadkiem szczególnym liczby zmiennoprzecinkowej IEEE 754), to jej wartość liczbowa zawiera się pomiędzy 1 a 2. Wynika stąd, iż pierwszy bit całkowity mantysy zawsze wynosi 1. Skoro tak, to nie musi on być zapamiętywany - będzie automatycznie odtwarzany w czasie wykonywania obliczeń na liczbie zmiennoprzecinkowej. W polu mantysy zapamiętujemy tylko bity ułamkowe. Dzięki tej prostej sztuczce zyskujemy jeden dodatkowy bit mantysy - zwiększamy jej rozdzielczość do 24 bitów dla formatu pojedynczej precyzji i do 53 bitów dla formatu podwójnej precyzji. Sposób ten nie jest wcale nowy - zastosował go już w 1936 roku niemiecki konstruktor Konrad Zuse w swoich komputerach.
Wartość liczby IEEE 754 obliczamy wg poznanych dotychczas zasad. Z kodu wydzielamy poszczególne pola znaku z, cechy c i mantysy m. Odczytana mantysa zawiera tylko bity ułamkowe. Dodajemy zatem na początku 01 i przecinek. W wyniku otrzymujemy dodatnią liczbę stałoprzecinkową w kodzie U1. Teraz obliczamy wartość cechy i mantysy, a następnie wyliczamy wartość liczby wg wzoru:
Dla przykładu policzymy wartości kilku liczb w pojedynczej precyzji (dla podwójnej precyzji rachunki są identyczne, lecz bardziej uciążliwe do przeprowadzenia z uwagi na większą liczbę bitów).
Przykład:
Obliczyć wartość dziesiętną liczby zmiennoprzecinkowej: 01000010110010000000000000000000(IEEE 754).
Kod binarny dzielimy na poszczególne pola zawierające kolejno znak, cechę oraz bity ułamkowe mantysy:
0 | 10000101 | 10010000000000000000000 |
z | cecha | bity ułamkowe mantysy |
z = 0
- liczba jest dodatnia c = 10000101(BIAS=127) = 133 - 127 = 6 m = 01,10010000000000000000000(U1) = 19/16 |
Mamy wszystkie niezbędne składniki, wyliczamy wartość liczby:
L(IEEE 754) = (-1)zm2c
= (-1)0 ·
19/16 · 26
=
25/16 · 26= 25 · 22
= 25 · 4 = 100(10) 01000010110010000000000000000000(IEEE 754) = 100(10). |
Przykład:
Obliczyć wartość dziesiętną liczby zmiennoprzecinkowej : 11000001110110000000000000000000(IEEE 754).
Postępujemy identycznie jak w poprzednim przykładzie:
1 | 10000011 | 10110000000000000000000 |
z | cecha | bity ułamkowe mantysy |
z = 1
- liczba jest ujemna c = 10000011(BIAS=127) = 131 - 127 = 4 m = 01,10110000000000000000000(U1) = 111/16 L(IEEE 754) = (-1)zm2c = (-1)1 · 111/16 · 24 = - 27/16 · 24= -27(10) 11000001110110000000000000000000(IEEE 754) = -27(10). |
Przykład:
Obliczyć wartość dziesiętną liczby zmiennoprzecinkowej : 10111110000000000000000000000000(IEEE 754).
1 | 01111100 | 00000000000000000000000 |
z | cecha | bity ułamkowe mantysy |
z = 1
- liczba jest ujemna c = 01111100(BIAS=127) = 124 - 127 = -3 m = 01,00000000000000000000000(U1) = 1 |
L(IEEE 754) = (-1)zm2c
= (-1)1 · 1 · 2-3 = -
1/8
= -0,125(10) 10111110000000000000000000000000(IEEE 754) = -0,125(10). |
Liczby zmiennoprzecinkowe wcale nie są takie straszne - pod warunkiem, że pilnie studiowałeś nasz artykuł. W przeciwnym razie na zawsze pozostaną dla ciebie tajemnicą...
Poniżej przedstawiam prosty program w języku Pascal, który pozwala zobaczyć wewnętrzną postać wprowadzonej liczby zmiennoprzecinkowej w standardzie IEEE 754 (program nie uwzględnia wartości specjalnych - ale może ty potrafisz go ulepszyć?). W programie wykorzystujemy fakt, iż komputer IBM PC przechowuje liczby zmiennoprzecinkowe w tym właśnie standardzie, zatem nic nie musimy przeliczać, wszystko robi za nas system.
C++// Format IEEE 754 #include <iostream> #include <string> #include <iomanip> using namespace std; union sp_ieee754 { unsigned char b[4]; float f; }; main() { sp_ieee754 l_ieee754; int i,j,v,c,z; string s; char zz[1]; cout << setprecision(10) << fixed; cout << "Demonstracja standardu zmiennoprzecinkowego IEEE 754\n" "----------------------------------------------------\n" "(C)2005 mgr Jerzy Walaszek I LO w Tarnowie\n\n" "Wpisz liczbe : "; cin >> l_ieee754.f; cout << endl; //--------------------------------------- // Odczytujemy kolejne bity zapisu liczby //--------------------------------------- s = ""; for(i = 0; i < 4; i++) { v = l_ieee754.b[i]; for(j = 0; j < 8; j++) { s = char(48 + (v % 2)) + s; v /= 2; } } cout << s << endl << endl; for(i = 0; i < 32; i++) { cout << s[i]; if((i == 0) || (i == 8)) cout << " "; } cout << "\nz cecha mantysa\n\n"; //---------------------------------------------------- // Teraz wyznaczamy wartości poszczególnych składników // Program nie uwzględnia wartości specjalnych ! //---------------------------------------------------- z = (l_ieee754.b[3] & 0x80) >> 7; cout << "z = " << z << endl; c = (((l_ieee754.b[3] & 0x7f) << 1) | ((l_ieee754.b[2] & 0x80) >> 7)) - 127; cout << "c = " << s.substr(1,8) << " = " << c << endl; v = 0x800000 | ((l_ieee754.b[2] & 0x7f) << 16) | (l_ieee754.b[1] << 8) | l_ieee754.b[0]; cout << "m = 01," << s.substr(9,23) << " = " << v / (double)0x800000 << endl; cout << "\nKlawisz ENTER = KONIEC\n"; cin.getline(zz,1); } |
Pascalprogram IEEE754; {$APPTYPE CONSOLE} type sp_ieee754 = record case value: integer of 0 : (b : array[0..3] of byte); 1 : (f : single); end; var l_ieee754 : sp_ieee754; i,j,v,c,z : integer; s : string; begin writeln('Demonstracja standardu zmiennoprzecinkowego IEEE 754'); writeln('----------------------------------------------------'); writeln('(C)2005 mgr Jerzy Walaszek I LO w Tarnowie'); writeln; write('Wpisz liczbe : '); readln(l_ieee754.f); writeln; //--------------------------------------- // Odczytujemy kolejne bity zapisu liczby //--------------------------------------- s := ''; for i := 0 to 3 do begin v := l_ieee754.b[i]; for j := 1 to 8 do begin s := char(48 + (v mod 2)) + s; v := v div 2; end; end; writeln(s); writeln; for i := 1 to 32 do begin write(s[i]); if (i = 1) or (i = 9) then write(' '); end; writeln; writeln('z cecha mantysa'); writeln; //---------------------------------------------------- // Teraz wyznaczamy wartości poszczególnych składników // Program nie uwzględnia wartości specjalnych ! //---------------------------------------------------- z := (l_ieee754.b[3] and $80) shr 7; writeln('z = ',z); c := ((l_ieee754.b[3] and $7f) shl 1) or ((l_ieee754.b[2] and $80) shr 7) - 127; writeln('c = ',copy(s,2,8),' = ',c); v := $800000 or ((l_ieee754.b[2] and $7f) shl 16) or (l_ieee754.b[1] shl 8) or l_ieee754.b[0]; writeln('m = 01,',copy(s,10,23),' = ',v / $800000:0:10); writeln; write('Klawisz ENTER = KONIEC'); readln; end. |
Wynik: |
Demonstracja standardu
zmiennoprzecinkowego IEEE 754 ---------------------------------------------------- (C)2005 mgr Jerzy Walaszek I LO w Tarnowie Wpisz liczbe : -13257.25 11000110010011110010010100000000 1 10001100 10011110010010100000000 z cecha mantysa z = 1 c = 10001100 = 13 m = 01,10011110010010100000000 = 1.6183166504 Klawisz ENTER = KONIEC |
Gdy znamy budowę liczby zmiennoprzecinkowej w standardzie IEEE 754, z obliczeniem zakresu możliwych do zapisania w nim liczb nie będzie żadnego problemu. Po prostu musimy znaleźć największą cechę i największą mantysę i podstawić je do podanego wcześniej wzoru.
Największa cecha posiada kod:
c = 11111110(BIAS=127) c = 254 - 127 c = 127 |
Kod
Największa mantysa osiada kod:
m = 01,11111111111111111111111(U1) m = (224 - 1) / 223 |
Obliczamy wartość największej liczby:
max(IEEE 754)
= m · 2c max(IEEE 754) = (224 - 1) / 223 · 2127 max(IEEE 754) = (224 - 1) · 2104 max(IEEE 754) = 3,4028234663852885981170418348452 · 1038 |
a po zaokrągleniu:
max(IEEE 754) = 3,4 · 1038 |
Ponieważ zmianą znaku możemy uzyskać tę samą wartość po stronie ujemnej, zakres liczb zmiennoprzecinkowych IEEE 754 w pojedynczej precyzji wynosi:
Zapamiętaj:Zakres liczb zmiennoprzecinkowych IEEE 754 w pojedynczej precyzji
|
Dla liczby IEEE 754 podwójnej precyzji przeprowadzamy identyczne obliczenia:
c = 11111111110(BIAS=1023) c = 2046 - 1023 c = 1023 m = 01,1111111111111111111111111111111111111111111111111111(U1) m = (253 - 1) / 252 max(IEEE 754) = m · 2c max(IEEE 754) = (253 - 1) / 252 · 21023 max(IEEE 754) = (253 - 1) · 2971 max(IEEE 754) = 1,797693134862315708145274237317 · 10308 |
a po zaokrągleniu:
max(IEEE 754) = 1,8 · 10308 |
Zapamiętaj:Zakres liczb zmiennoprzecinkowych IEEE 754 w podwójnej precyzji
|
Oprócz zakresu, na który wpływa głównie długość formatu bitowego cechy, dla liczb zmiennoprzecinkowych nie mniej ważnym parametrem jest precyzja zapisu, czyli z jaką dokładnością dana liczba może być reprezentowana. Precyzję podaje się najczęściej jako przybliżoną ilość dziesiętnych cyfr znaczących. Na przykład jeśli precyzja zapisu wynosi trzy cyfry dziesiętne, to liczby 856 92,4 1,53 zostaną zapamiętane dokładnie, natomiast liczby o większej ilości cyfr już nie: 85613 zostanie zapamiętane jako 856??, gdzie znaki ? reprezentują dowolne cyfry.
Precyzję zapisu liczby zawsze wyznacza ilość dostępnych bitów mantysy. Jedną
cyfrę dziesiętną koduje (statystycznie)
Dla pojedynczej precyzji mantysa ma długość 24 bity:
1 cyfra dziesiętna
--- log2(10) bitów x cyfr dziesiętnych --- 24 bity |
Stąd
x cyfr dziesiętnych = 24 / log2(10) = 24 / 3,32 = 7,2 cyfry |
Zaokrąglamy wynik do liczby całkowitej i dla liczb w pojedynczej precyzji otrzymujemy 7 dziesiętnych cyfr znaczących.
Dla podwójnej precyzji mantysa ma długość 53 bity:
1
cyfra
dziesiętna --- log2(10) bitów x cyfr dziesiętnych --- 53 bity |
Stąd
x cyfr dziesiętnych = 53 / log2(10) = 53 / 3,32 = 15,96 cyfry |
Zaokrąglamy wynik do liczby całkowitej i dla liczb w podwójnej precyzji otrzymujemy 15-16 cyfr znaczących.
Zapamiętaj:Liczby pojedynczej precyzji IEEE 754 oferują precyzję 7 cyfr dziesiętnych. Zatem nadają się one do bardzo prostych rachunków, gdzie nie wymagana jest zbyt duża dokładność. Liczby podwójnej precyzji IEEE 754 oferują precyzję 15...16 cyfr dziesiętnych. Zatem nadają się do dokładnych obliczeń naukowych i inżynierskich. Stosuj je zawsze tam, gdzie wymagana jest duża dokładność końcowych wyników. |
Zauważ, iż w normalny sposób nie można zapisać w formacie IEEE 754 wartości 0, ponieważ mantysa ma domyślną część całkowitą równą 1 - w polu mantysy zapamiętywane są jedynie bity ułamkowe. Dlatego zero jest specjalnym przypadkiem liczby zmiennoprzecinkowej, gdzie zarówno pole wykładnika jak i mantysy zawiera same 0. Bit znaku może przyjmować dowolną wartość (stąd możemy dostać dodatnie lub ujemne 0, jednakże przy porównaniu są one traktowane jak równe sobie).
Wartość ZERO | pole znaku |
pole cechy |
pole mantysy |
---|---|---|---|
0/1 | 0...0 | 0...0 |
Jeśli wszystkie bity cechy mają wartość 0, lecz mantysa zawiera bity o wartościach 1 (w przeciwnym razie liczba zostanie potraktowana jak opisane wcześniej zero), to jest to tzw. zdenormalizowana liczba zmiennoprzecinkowa. W takim przypadku mantysa nie posiada domyślnej części całkowitej 1, lecz jest liczbą ułamkową, której bity zawarte są w polu formatu IEEE 754.
Wartość zdenormalizowana |
pole znaku |
pole cechy |
pole mantysy |
---|---|---|---|
0/1 | 0...0 | bity ułamkowe mantysy |
Wartość zdenormalizowaną liczby zmiennoprzecinkowej liczymy według wzoru:
m = 00,(pole mantysy)(U1) L = (-1) z · m · 2 -126 |
Stąd najmniejszą wartością zmiennoprzecinkową różną od zera jest:
00000000000000000000000000000001(IEEE 754) m = 00,00000000000000000000001(U1) m = 2-23 min(IEEE 754) = 2-23 · 2-126 = 2-149 min(IEEE 754) = 1,4012984643248170709237295832899 · 10-45 min(IEEE 754) = ±1,4 · 10-45 |
m = 00,(pole mantysy)(U1) L = (-1) z ·m · 2 -1022 |
Stąd najmniejszą wartością zmiennoprzecinkową różną od zera jest:
000000000000000000000000000000000000000000000000000000000000001(IEEE
754) m = 00,0000000000000000000000000000000000000000000000000001(U1) m = 2-52 min(IEEE 754) = 2-52 · 2-1022 = 2-1074 min(IEEE 754) = 4,9406564584124654417656879286822 · 10-324 min(IEEE 754) = ±4,9 · 10-324 |
Zapamiętaj:Wartości zdenormalizowane pozwalają przedstawiać bardzo małe liczby zmiennoprzecinkowe, które bez tej opcji zostałyby zaokrąglone do 0. Zwiększa to precyzję wykonywanych operacji zmiennoprzecinkowych i jest cechą bardzo pożądaną. Liczbę zero można traktować jak liczbę zdenormalizowaną, w której mantysa wynosi 0. |
Standard IEEE 754 pozwala reprezentować dodatnią i ujemną nieskończoność. Pole znaku określa, z którą nieskończonością mamy do czynienia - dodatnią czy ujemną. Cecha posiada wszystkie bity ustawione na 1 (maksymalna wartość cechy), a bity mantysy są wyzerowane.
Nieskończoność | pole znaku |
pole cechy |
pole mantysy |
---|---|---|---|
0/1 | 1...1 | 0...0 |
Wartość typu nieskończoność jest bardzo pożądana w systemie zmiennoprzecinkowym, ponieważ pozwala ona prowadzić obliczenia po wystąpieniu nadmiaru. Wyniki działań z nieskończonościami są dokładnie zdefiniowane w standardzie IEEE 754 - opisujemy je na końcu rozdziału.
Standard IEEE 754 definiuje dwie specjalne wartości, które nie reprezentują liczb. Stąd nazywane są w literaturze terminem NaN (Not a Number - Nie Liczba). Nieliczby mogą być dwojakiego rodzaju - tzw. ciche - QNaN (ang. Quiet NaN) lub głośne SNaN (ang. Signaling NaN). W obu przypadkach cecha zawiera same 1, a pole mantysy zawiera bity 1. Jeśli najstarszy bit ułamkowy mantysy jest ustawiony na 1, to kod reprezentuje cichą nieliczbę - QNaN.
Cicha Nieliczba QNaN |
pole znaku |
pole cechy |
pole mantysy |
---|---|---|---|
0/1 | 1...1 | 1X...X |
Jeśli najstarszy bit mantysy ustawiony jest na 0, kod reprezentuje głośną nieliczbę:
Głośna Nieliczba SNaN |
pole znaku |
pole cechy |
pole mantysy |
---|---|---|---|
0/1 | 1...1 | 0X...X |
Nieliczby mają wiele zastosowań. Ciche nieliczby przechodzą przez działania arytmetyczne. Najczęściej oznaczają one wartość niezdefiniowaną. Głośne nieliczby powodują powstanie wyjątków w operacjach arytmetycznych. Najczęściej oznaczają one wartość niedozwoloną. Typowym zastosowaniem głośnych nieliczb jest wstępna inicjalizacja zmiennych. Jeśli programista nie wprowadzi do zmiennej wartości, czyli nie zainicjuje jej, to będzie ona zawierała wartość SNaN. Próba użycia takiej zmiennej w jakimkolwiek wyrażeniu arytmetycznym spowoduje wygenerowanie wyjątku i przerwanie obliczeń.
Standard IEEE 754 bardzo dokładnie precyzuje wyniki operacji, w których jednym z argumentów (lub obydwoma) jest wartość specjalna. W poniższej tabelce zebraliśmy najważniejsze zasady przeprowadzania takich operacji:
Operacja | Wynik |
---|---|
![]() |
0 |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
NaN |
![]() |
NaN |
![]() |
NaN |
![]() |
NaN |
Zainteresowanych tym tematem odsyłamy do artykułu o komputerach Konrada Zuse.
W poniższej tabelce podsumowujemy wszystkie możliwe wartości reprezentowane
przez format IEEE 754. Więcej danych na temat
standardu
Interpretacja kodu IEEE 754 | |||
---|---|---|---|
Pole znaku |
Pole cechy |
Pole mantysy |
Wartość |
0 | 0...0 | 0..0 | +0 (zero dodatnie) |
0 | 0...0 | 0...1 : 1...1 |
Dodatnia liczba zdenormalizowana |
0 | 0...1 : 1...0 |
0...0 : 1...1 |
Dodatnia liczba zmiennoprzecinkowa |
0 | 1...1 | 0...0 | ![]() |
0 | 1...1 | 0...01 : 01...1 |
SNaN (głośna nieliczba) |
0 | 1...1 | 10...0 : 1...1 |
QNaN (cicha nieliczba) |
1 | 0...0 | 0...0 | -0 (zero ujemne) |
1 | 0...0 | 0...1 : 1...1 |
Ujemna liczba zdenormalizowana |
1 | 0...01 : 1...10 |
0...0 : 1...1 |
Ujemna liczba zmiennoprzecinkowa |
1 | 1...1 | 0...0 | ![]() |
1 | 1...1 | 0...01 : 01...1 |
SNaN (głośna nieliczba) |
1 | 1...1 | 10...0 : 1...1 |
QNaN (cicha nieliczba) |
Każdy współczesny procesor Pentium zintegrowany jest wewnętrznie z koprocesorem arytmetycznym, który wykonuje sprzętowo operacje na liczbach zmiennoprzecinkowych. Wewnętrznie stosuje on rozszerzony typ danych zmiennoprzecinkowych o następującej specyfikacji formatu:
Format rozszerzony zapisu zmiennoprzecinkowego koprocesora arytmetycznego | |||
---|---|---|---|
80 bitów - rozszerzona precyzja | (1 bit) b79 | (15 bitów) b78 ... b64 (BIAS=16383) | (64 bity) ,b63 ... b0 (U1) |
Opis pół bitowych | bit znaku | bity kodu cechy | bity ułamkowe mantysy |
Na podstawie materiału przedstawionego w tym rozdziale wyznacz kolejno:
Zobacz dalej...
Przykładowy system zmiennoprzecinkowy
| Wywiad z twórcą standardu IEEE 754, Williamem Kahanem
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2023 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.