zmiennoprzecinkowe_v07.pdf

(149 KB) Pobierz
LICZBY ZMIENNOPRZECINKOWE
Liczby zmiennoprzecinkowe są komputerową reprezentacją liczb rzeczywistych zapisanych w formie wykładniczej
(naukowej). Aby uprościć arytmetykę na nich, przyjęto ograniczenia zakresu mantysy i eksponenty oraz wprowadzono
inne założenia, które reguluje norma IEEE 754 (dla liczb zapisanych w kodzie dwójkowym).
Liczbę zapisuje się jako ciąg zer i jedynek przyjmując umowny podział na “pola”:
w jednostce zmiennoprzecinkowej
S
E
M
G R X/S
znak wykładnik w kodzie spolaryzowanym
ułamek
bity specjalne
podział liczby zmiennoprzecinkowej na pola
S – znak, jest zawsze jedno bitowy i ma wartość 0 jeśli liczba jest dodatnia lub 1 jeśli jest ujemna
E – wykładnik (inaczej: eksponent, cecha), ma długość zależną od długości całej liczby i kodowane jest w kodzie
2 k- 1 -1
M – moduł (inaczej mantysa) ułamka. Ma wartość z przedziału [1,2). Zapisuje się go bez poprzedzającej go jedynki
z kropką (tzw. bit ukryty). Innymi słowy, zapisujemy jedynie część ułamkową modułu przyjmując, że zawsze
(pomijając wyjątki, o których później) część całkowita wynosi 1.
Bity specjalne występują jedynie w wewnętrznej reprezentacji liczy w jednostce zmiennoprzecinkowej
i wykorzystywane są do zniwelowania efektu utraty dokładności przy wykonywaniu działań, przez zaokrąglenie.
Zarówno liczba wejściowa jak i wyjściowa takiego układu nie posiada tych bitów. Oznacza to, że liczby 32/64/128
bitowe, w jednostce zmiennoprzecinkowej, po uzupełnieniu bitami specjalnymi, maja długość odpowiednio 35/67/131
bitów. Bity te są używane tylko dla pojedynczej operacji arytmetycznej po czym wykonywane jest zaokrąglenie.
Mówiąc inaczej, w szeregu operacji arytmetycznych, po każdej pojedynczej operacji następuje zaokrąglenie
i
porzucenie bitów GRX (gdyż nie ma na nie miejsca poza jednostką zmiennoprzecinkową),
G – bit ochrony (guard)
R – bit zaokrąglenia (round)
X – bit obecności zer na wszystkich pozycjach mniej znaczących od R (sticky)
rozmiar liczby wykładnik E moduł ułamka M nazwa
32
8
23
single
64
11
52
double
128
16
111
extended
długości poszczególnych pól (w bitach)
Wartość reprezentowaną przez liczbę określa się wg wzoru:
x = S −1 M ⋅2 E
PRZYKŁADY:
a) 0 0100 0100 111 1100 1010 0010 0111 1100 = 1,111110010100 01001111100⋅2 −59
= 1 ⋅1,8168060 ⋅2 −59
DŁUGOŚĆ: 32 bity
s (1 bit) = 0 (liczba dodatnia)
E (8 bit) = 0100 0100 = 68-127= -59
M = 1,111 1100 1010 0010 0111 1100 2 = 1,8168060 10
b) 1 0100 0100 111 1100 1010 0010 0111 1100 = −1,11111001010 001001111100⋅2 −59
=
−1 ⋅1,8168060 ⋅2 −59
DŁUGOŚĆ: 32 bity
s (1 bit) = 1 (liczba ujemna)
E (8 bit) = 0100 0100 = 68-127= -59
M = 1,111 1100 1010 0010 0111 1100 2 = 1,8168060 10
c) 0 0111 1111 000 0000 0000 0000 0000 0000 = 1 ⋅1,0 ⋅2 0
= 1
DŁUGOŚĆ: 32 bity
s (1 bit) = 0 (liczba dodatnia)
E (8 bit) = 0111 1111 = 127-127 = 0
M = 1,000 0000 0000 0000 0000 0000 2 = 1,0 10
Krzysztof Adamski :: http://mr-k.namyslow.eu.org/
787972219.051.png 787972219.062.png 787972219.073.png 787972219.084.png 787972219.001.png 787972219.002.png 787972219.003.png 787972219.004.png 787972219.005.png 787972219.006.png 787972219.007.png 787972219.008.png 787972219.009.png 787972219.010.png 787972219.011.png 787972219.012.png 787972219.013.png 787972219.014.png 787972219.015.png 787972219.016.png 787972219.017.png 787972219.018.png 787972219.019.png 787972219.020.png
TIPS & TRICKS
czyli jak ułatwić sobie życie (czytaj: obliczenia)
Przekodowanie liczb w systemie 2
k-1
-1 na U2
Po co? Ano dla tego, że kochamy U2, w nim wszystko jest proste i traktujemy ten kod prawie tak samo naturalnie jak
zwykły dziesiętny.
Co nam to ułatwi? Np. dzielenie przez dwa wykonywane przy pierwiastkowaniu.
Więc do rzeczy... Przekodowanie liczby z zachowaniem znaku jest trudniejsze a nam zachowanie znaku nie jest
potrzebne (jak dodamy/odejmiemy dwie liczby ujemne lub podzielimy liczbę ujemna a potem zmienimy znów znak to
co do modułu utrzymamy ten sam wynik) więc będziemy przekodowywać ze zmianą znaku.
Sprawa jest niezwykle banalna – należy zanegować wszystkie bity poza najstarszym (najmniej znaczącym albo, jak kto
woli, tym najbardziej na lewo). Oto przykład:
NB
2 k-1 -1
U2 (liczba przeciwna)
k
10
10+127=137 10 = 1 0001001
1 1110110 U2 =-10 10
8
-25
-25+127=102 10 = 0 1100110
0 0011001 U2 =25 10
8
0
0+127=127 10 = 0 1111111
0 0000000 U2 =0 10
8
Zmiana znaku liczb w systemie 2
k-1
-1:
Jak zostało pokazane powyżej, negacja wszystkich bitów poza najstarszym zmienia liczbę na U2 o przeciwnym znaku.
Operacja odwrotna również zmienia znak więc możemy wykonać następujący algorytm:
przekodowanie liczby na U2 (negacja wszystkich bitów poza pierwszym)
zmiana znaku (negacja wszystkich bitów i dodanie 1)
przekodowanie na 2 k-1 -1 (negacja wszystkich bitów poza pierwszym)
Algorytm ten można jednak zoptymalizować. Pierwszy bit zanegowany jest raz a wszystkie pozostałe 3 krotnie.
Podwójne zanegowanie nie daje żadnego rezultatu więc ostatecznie wszystkie bity są negowane tylko raz. Po
przekodowaniu na U2 dodajemy jedynkę a później negujemy więc w ostatecznym rozrachunku jedynka ta jest
odejmowana. Po uwzględnieniu tych faktów napisać można zoptymalizowany algorytm:
zanegować wszystkie bity
odjąć 1 od najmłodszego bitu
Przykład:
NB
2 k-1 -1
zanegowana
odjęte 1
k
10
10+127=137 10 =10001001
01110110
01110101=117-127 = -10 10
8
-25
-25+127=102 10 =01100110
10011001
10011000=152-127 = 25 10
8
0
0+127=127 10 =01111111
10000000
01111111=127-127 = 2 10
8
sposób 2:
W systemie z obciążeniem wartość powiększona jest o liczbę O (będącą właśnie tym obciążeniem). W związku z tym
nasza liczba x przedstawiona jest jako:
E = x + O
My chcemy znaleźć liczbę -x na podstawie danej nam liczby E i zapisać ją również w z obciążeniem. Szukana przez nas
wartość powinna wynosić:
-x + O = -(x + O) + 2O = -E + 2O = 2O – E
Wynika z tego, że aby zmienić znak liczby w kodzie z obciążeniem musimy ją odjąć od podwojonego obciążenia.
Brzmi to trudniej niż jest w rzeczywistości. Dla liczb zmiennoprzecinkowych używamy obciążenia 2 k-1 -1 (gdzie k to
liczba bitów przypadających na eksponentę. Dla standardu pojedynczej precyzji (8 bit na wykłądnik) obciążenie
O=127 10 =01111111 2 . Pomnożenie liczby przez 2 to przesunięcie jej o jedną pozycję w lewo więc 2O=11111110 2 . I od
tej liczby należy odjąć naszą wartość obciążoną by dostać jej wartość przeciwną.
Krzysztof Adamski :: http://mr-k.namyslow.eu.org/
787972219.021.png 787972219.022.png 787972219.023.png 787972219.024.png 787972219.025.png 787972219.026.png 787972219.027.png 787972219.028.png 787972219.029.png 787972219.030.png 787972219.031.png 787972219.032.png 787972219.033.png 787972219.034.png 787972219.035.png 787972219.036.png 787972219.037.png 787972219.038.png 787972219.039.png 787972219.040.png 787972219.041.png 787972219.042.png 787972219.043.png 787972219.044.png 787972219.045.png 787972219.046.png 787972219.047.png 787972219.048.png 787972219.049.png 787972219.050.png 787972219.052.png 787972219.053.png 787972219.054.png 787972219.055.png 787972219.056.png
KODY SPECJALNE
Do pewnych specjalnych zastosowań zarezerwowano kody, w których wykładnik ma wartość minimalną (same zera)
lub maksymalną (same jedynki).
ZERO:
Ponieważ przy założeniu, że mantysa jest znormalizowana (ma wartość z przedziału [1,2)), nie da się zapisać zera, na tą
liczbę zarezerwowano specjalne kody, w których wszystkie bity poza pierwszym (S) są zerami.
Wyróżnia się więc zero dodatnie (przykład dla liczb 32-bitowych):
+0 = 0 0000 0000 000 0000 0000 0000 0000 0000
I zero ujemne (przykład dla liczb 32-bitowych):
-0 = 1 0000 0000 000 0000 0000 0000 0000 0000
LICZBY BLISKIE ZERU:
Kolejną konsekwencją normalizacji mantysy jest to, że nie da się zapisać liczb z przedziału (-2 Emin , +2 Emin ). Na potrzeby
tych liczb, zarezerwowano kody, w których wykładnik jest równy zero ale mantysa jest różna od zera. W przypadku
tych liczb, przyjmuje się, że mantysa ma bit ukryty równy 0 a nie 1 ( 0<M<1 ), wykładnik jest natomiast równy -2 k-1 +2.
Przykłady:
a) 0 0000 0000 111 1100 1010 0010 0111 1100 = 1⋅0,8168060⋅2 −126
M = 0 ,111 1100 1010 0010 0111 1100 2 = 0 ,8168060 10
E (8 bit) = 0000 0000 = -126 !!
a) 1 0000 0000 111 1100 1010 0010 0111 1100 = −1⋅0,8168060⋅2 −126
M = 0 ,111 1100 1010 0010 0111 1100 2 = 0 ,8168060 10
E (8 bit) = 0000 0000 = -126
NIE-LICZBY:
Nie-liczby (NaN – Not A Number) to kody reprezentujące wyniki niedające się zakodować w postaci liczby
zmiennoprzecinkowej (np. wynik pierwiastkowania kwadratowego liczby ujemnej).
Nie-liczbę kodujemy za pomocą liczby z wykładnikiem składającym się z samych jedynek i modułem różnym od zera.
Wyróżnia się nie-liczby ciche (QNaN) wytwarzane sprzętowo oraz nie-liczby sygnalizacyjne (SNaN) generowane
w programie obsługi wyjątku. Pierwsze charakteryzują się kodem ułamka zaczynającym się od 1xxxx a drugie kodem
ułamka zaczynającym się od 0xxxx.
Przykład kodów NaN (dla liczb 32-bitowych):
1 1111 1111 111 1111 1111 1111 1111 1111 (QNaN)
0 1111 1111 000 0000 0000 0000 0000 0001 (SNaN)
NIESKOŃCZONOŚCI :
Za pomocą liczb zmiennoprzecinkowych można również zakodować nieskończoności. Kodem zarezerwowanym dla
tych wartości jest wykładnik składający się z samych jedynek i moduł z samych zer.
Wyróżnia się minus nieskończoność (przykład dla kodu 32-bitowego):
-∞ = 1 1111 1111 000 0000 0000 0000 0000 0000
Plus nieskończoność (przykład dla kodu 32-bitowego):
∞ = 0 1111 1111 000 0000 0000 0000 0000 0000
PODSUMOWANIE:
Wykładnik
Mantysa
Rodzaj liczby
00....00
00....00
zero
00....00
>0
Liczby bliskie zeru
11....11
1x....xx
QNaN
11....11
0x....xx
SNaN
11....11
00....00
Nieskończoność
Krzysztof Adamski :: http://mr-k.namyslow.eu.org/
787972219.057.png 787972219.058.png 787972219.059.png 787972219.060.png 787972219.061.png 787972219.063.png 787972219.064.png 787972219.065.png 787972219.066.png 787972219.067.png 787972219.068.png 787972219.069.png 787972219.070.png 787972219.071.png 787972219.072.png 787972219.074.png 787972219.075.png 787972219.076.png 787972219.077.png 787972219.078.png 787972219.079.png 787972219.080.png 787972219.081.png 787972219.082.png 787972219.083.png 787972219.085.png 787972219.086.png 787972219.087.png 787972219.088.png 787972219.089.png
 
DZIAŁANIA ARYTMETYCZNE NA LICZBACH ZMIENNOPRZECINKOWYCH
Aby zrozumieć algorytmy działań arytmetycznych na liczbach zmiennoprzecinkowych należy wyobrazić je sobie
w postaci wykładniczej:
x=M*B E
i odnieść się do praw arytmetyki działających na liczbach takiej postaci.
DODAWANIE/ODEJMOWANIE:
Z postaci liczb zmiennoprzecinkowych wynika lekka komplikacja przy operacjach dodawania/odejmowania. Aby je
wykonać należy najpierw sprowadzi składniki do wspólnego wykładnika. Weźmy dwie liczby:
x 1 =M 1 *B E1
x 2 =M 2 *B E2
Jako, że dodawanie jest przemienne (a odejmowanie to też dodawanie) przyjmijmy, że E1>E2. Wynik otrzymujemy wg
wzoru:
x 1 ± x 2 = M 1 *B E1 ± M 2 *B E2 = (M 1 *B E1-E2 ± M 2 )*B E2
Jak to zrobić na papierze:
1. Wyrównaj wykładniki:
Wykładniki muszą być równe więc jeden ze składników sumy musi zostać przeskalowany. Oczywiście
zależy nam na jak najmniejszej utracie dokładności więc powinniśmy tracić najmniej znaczące bity co
implikuje przesuwanie mantysy w prawo co wymusza zrównanie wykładnika liczby mniejszej do
większej.
Przesunięcie mantysy o jeden bit w prawo równoważymy dodaniem 1 do wykładnika.
Należy pamiętać o bicie ukrytym.
2. Dodaj mantysy:
Skoro wykładniki są sobie równe możemy śmiało dodać mantysy, pamiętając o bicie ukrytym .
3. Znormalizuj wynik:
Jak już wspomniałem może się okazać koniecznym znormalizowanie wyniku co będzie oznaczać
przesunięcie mantysy w prawo (zwiększając wykładnik) bądź w lewo (zmniejszając wykładnik).
4. Zapisz wynik:
Pamiętaj o ukryciu bitu.
MNOŻENIE:
W przeciwieństwie do dodawania/odejmowania mnożenie liczb w postaci zmiennoprzecinkowej jest banalne. Dla liczb:
x 1 =M 1 *B E1
x 2 =M 2 *B E2
wynik mnożenia wynosi:
x 1 *x 2 = (M 1 *B E1 ) * (M 2 *B E2 ) = (M 1 *M 2 )*(B E1 *B E2 ) = (M 1 *M 2 )*B E1+E2
Jak to zrobić na papierze:
1. Wymnóż mantysy:
Pamiętaj o bicie ukrytym.
2. Dodaj wykładniki:
Wykładniki są zapisane w systemie spolaryzowanym co oznacza, że po ich zsumowaniu w wyniku
otrzymamy podwójne obciążenie co wymaga korekcji (przez odjęcie obiciążenia).
3. Znormalizuj wynik:
Przesunięcie mantysy w prawo (zwiększenie wykładnika) bądź w lewo (zmniejszenie wykładnika).
4. Zapisz wynik:
Pamiętaj o ukryciu bitu.
DZIELENIE:
Dzielenie jest bardzo podobne do mnożenia:
x 1 /x 2 = (M 1 *B E1 ) / (M 2 *B E2 ) = (M 1 /M 2 )*(B E1 / B E2 ) = (M 1 / M 2 )*B E1-E2
Jak to zrobić to na papierze:
1. Podziel mantysy:
Pamiętaj o bicie ukrytym.
2. Odejmij wykładniki:
Krzysztof Adamski :: http://mr-k.namyslow.eu.org/
Wykładniki są zapisane w systemie spolaryzowanym co oznacza, że po ich odjęciu w wyniku otrzymamy
wynik bez obciążenia co wymaga korekcji (przez dodanie obciążenia).
3. Znormalizuj wynik:
Przesunięcie mantysy w prawo (zwiększenie wykładnika) bądź w lewo (zmniejszenie wykładnika).
4. Zapisz wynik:
Pamiętaj o ukryciu bitu.
OBLICZANIE ODWROTNOŚCI:
Weźmy liczbę:
x=M*B E
jej odwrotność wynosi:
1/x = x -1 = (M*B E ) -1 = M -1 * B -E = 1/M * B -E
Wynika z tego, że musimy osobno obliczyć odwrotność M i B E . Policzenie odwrotności ostatniej nie powinno stanowić
problemu ponieważ polega jedynie na zmianie znaku wykładnika (opisane wcześniej). Trochę gorzej jest
z odwrotnością mantysy.
Aby obliczyć odwrotność mantysy wykorzystamy własność: 1 x −1 ≈1− x dla x ≈0
Nasza mantysa jest liczbą w postaci 1+X (gdzie X to odczytana wprost wartość mantysy bez uwzględnienia ukrytej
jedynki). Możemy więc rozpatrzyć z osobna dwa przypadki:
X ≈0 :
Jeśli tak to możemy bezpośrednio skorzystać z podanego wyżej prawa i zapisać
M -1 =(1+X) =1 =1-X
Obliczenie odwrotności możemy więc obliczyć w 3 krokach:
negacja wszystkich bitów mantysy
dodanie 1 na najmłodszym bicie
normalizacja otrzymanej odwrotności (otrzymamy liczbę w postaci 0,xxxx więc trzeba przeskalować ją do postaci
1,xxxx jednocześnie odpowiednio zmniejszając wykładnik)
X ≈1
W takim wypadku możemy mantysę zapisać w postaci 2-X zamiast 1+X , w związku z tym nasza liczba ma wartość:
2− X ⋅2 E =2⋅1− X ⋅2 E =1− X ⋅2 E 1
A jej odwrotność:
[1− X ⋅2 E 1 ] −1 =1 X ⋅2 − E 1
Wynika z tego również, że liczba wynikowa będzie już znormalizowana. Dla nas oznacza to jednak, że aby obliczyć
odwrotność mantysy musimy jedynie:
podzielić mantysę przez 2 (przesunąć o jedną pozycję w prawo) uwzględniając bit ukryty
dodaj 2 do mantysy i zaneguj ją
w wyniku otrzymujemy liczbę postaci 1,xxxx więc nie potrzebna jest normalizacja.
Poznane metody są przybliżonymi i będą sprawdzać się jedynie przy liczbach bliskich minimum i maksimum wartości
mantysy. W innych nie możemy ich stosować ale na dziś nie chce mi się więcej pisać.
OBLICZANIE PIERWIASTKA KWADRATOWEGO:
Liczby zmiennoprzecinkowe mają za zadanie przechowywać liczby rzeczywiste więc pierwiastkowanie liczb ujemnych
jest niewykonalne. W związku z tym, pierwszy bit (bit znaku S) powinien być równy 0. W przeciwnym wypadku
wynikiem działania będzie zawsze NaN. Jego postać zależy od implementacji.
W przypad ku, gd y wykładnik jest parzysty sprawa jest prosta:
M B 2n = M B n
Gorzej jest gdy wykładnik nie jest parzysty ponieważ nie możemy łatwo spierwiastkować B E. . W takim wypadku
możemy przeskalować mantysę zmniejszając lub zwiększając wykładnik o jeden (dzięki czemu będzie on parzysty).
Lepszym pomysłem jest zmniejszenie wykładnika dzięki czemu w wyniku pierwiastkowania otrzymamy już liczbę
znormalizowaną i nie trzeba będzie jej znów skalować.
Przykłady:
a) 0 0100 0100 111 1100 1010 0010 0111 1100 = 1,111 1100 1010 0010 0111 1100 ⋅2 −59
Wartość liczby zmiennoprzecinkowej:
E (8 bit) = 0100 0100 = 68-127= -59
M = 1,111 1100 1010 0010 0111 1100
Wykładnik jest nieparzysty co oznacza, że nie możemy z niego łatwo wyciągnąć pierwiastka więc przeskalujemy mantysę.
Krzysztof Adamski :: http://mr-k.namyslow.eu.org/
787972219.090.png
Zgłoś jeśli naruszono regulamin