Treść:
Zdjęcie: http://pikabu.ru/
Wstęp
Już w pierwszych dniach nauki Javy natknąłem się na tak ciekawy rodzaj prymitywów jak liczby zmiennoprzecinkowe. Od razu zainteresowały mnie ich cechy, a jeszcze bardziej sposób, w jaki zostały zapisane w kodzie binarnym (który jest ze sobą połączony). W przeciwieństwie do dowolnego zakresu liczb całkowitych, nawet w bardzo małym zakresie (na przykład od 1 do 2) istnieje ich nieskończona liczba. A mając skończoną wielkość pamięci, nie da się wyrazić tego zestawu. Jak więc są wyrażone binarnie i jak działają? Niestety, wyjaśnienia na
wiki i całkiem fajny artykuł na temat Habré
tutaj nie dały mi pełnego zrozumienia, chociaż położyły podwaliny. Uświadomienie sobie tego przyszło dopiero po przeczytaniu tego
artykułu analitycznego następnego ranka po jego przeczytaniu.
Wycieczka w historię
(
zaczerpnięte z tego artykułu o Habré ) W latach 60-70, kiedy komputery były duże, a programy małe, nadal nie było jednego standardu obliczeń, a także standardu wyrażania samej liczby zmiennoprzecinkowej. Każdy komputer robił to inaczej i każdy miał swoje własne błędy. Jednak w połowie lat 70. Intel zdecydował się stworzyć nowe procesory z obsługiwaną „ulepszoną” arytmetyką i jednocześnie ją ujednolicić. Do jego opracowania sprowadzono profesorów Williama Kahana i Johna Palmera (nie, nie autora książek o piwie). Był pewien dramat, ale opracowano nowy standard. Teraz ten standard nazywa się IEEE754
Format liczb zmiennoprzecinkowych
Nawet w podręcznikach szkolnych każdy miał do czynienia z nietypowym sposobem zapisywania bardzo dużych lub bardzo małych liczb w postaci
1,2 × 10 3 lub
1,2 E3 , co równa się
1,2 × 1000 = 1200 . Nazywa się to metodą notacji wykładniczej. W tym przypadku mamy do czynienia z wyrażeniem liczby za pomocą wzoru:
N=M×n p , gdzie
- N = 1200 - liczba wynikowa
- M = 1,2 - mantysa - część ułamkowa, bez uwzględnienia rzędów
- n = 10 jest podstawą porządku. W tym przypadku i gdy nie mówimy o komputerach, podstawą jest liczba 10
- p = 3 - stopień podstawy
Dość często przyjmuje się, że podstawą rzędu jest 10
i zapisuje się tylko mantysę i wartość podstawy, oddzielając je literą
E. W naszym przykładzie podałem równoważne wpisy
1,2 × 10 3 i
1,2 E3 . Jeśli wszystko jest jasne i zakończyliśmy nostalgiczną wycieczkę do szkolnego programu nauczania, to teraz radzę o tym zapomnieć, ponieważ tworząc liczbę zmiennoprzecinkową mamy do czynienia z potęgi dwójki, a nie dziesiątek, tj.
n = 2 , cała harmonijna formuła
1.2E3 rozpada się i naprawdę złamał mi mózg.
Znak i stopień
Co więc mamy? W rezultacie mamy również liczbę binarną, która składa się z
mantysy – części, którą podniesiemy do potęgi i samej potęgi. Dodatkowo, podobnie jak w przypadku typów całkowitych, liczby zmiennoprzecinkowe posiadają bit określający znak – czy liczba będzie dodatnia, czy ujemna. Jako przykład proponuję rozważyć typ
float
, który składa się z 32 bitów. W przypadku liczb o podwójnej precyzji
double
logika jest taka sama, tylko jest dwa razy więcej bitów. Z 32 bitów pierwszy najbardziej znaczący jest przypisany do znaku, kolejne 8 bitów przydzielane jest do wykładnika - potęgi, do której podniesiemy mantysę, a pozostałe 23 bity - do mantysy. Aby to zademonstrować, spójrzmy na przykład:
Pierwszy bit jest bardzo prosty. Jeśli wartość pierwszego bitu
wynosi 0 , to otrzymana liczba będzie
dodatnia . Jeśli bit ma wartość
1 , liczba będzie
ujemna . Następny blok 8 bitów jest blokiem wykładniczym. Wykładnik zapisuje się jako zwykłą liczbę
ośmiobitową i aby uzyskać wymagany stopień, od otrzymanej liczby należy odjąć
127. W naszym przypadku osiem bitów wykładnika to
10000001 . Odpowiada to liczbie
129 . Jeśli masz pytanie, jak to obliczyć, zdjęcie pokazuje szybką odpowiedź. Wersję rozszerzoną można uzyskać w dowolnym kursie algebry Boole'a.
1×2 7 + 0×2 6 + 0×2 5 + 0×2 4 + 0×2 3 + 0×2 2 + 0×2 1 + 1×2 0 = 1×128 + 1×1 = 128+ 1=129 Nietrudno obliczyć, że maksymalna liczba, jaką możemy uzyskać z tych 8 bitów to
11111111 2 = 255 10 (indeks dolny
2 i
10 oznaczają systemy liczb binarnych i dziesiętnych) Jeśli jednak użyjemy tylko dodatnich wartości wykładniczych (
od 0 do 255 ), to wynikowe liczby będą miały wiele liczb przed przecinkiem dziesiętnym, ale nie po? Aby uzyskać ujemne wartości stopnia, należy od wygenerowanego wykładnika odjąć
127 . Zatem zakres stopni będzie wynosić
od -127 do 128 . Korzystając z naszego przykładu, wymagany stopień będzie wynosić
129-127 = 2 . Zapamiętajmy na razie tę liczbę.
Mantysa
Teraz o mantysie. Składa się z 23 bitów, ale na początku zawsze sugeruje się inną jednostkę, dla której bity nie są przydzielone. Odbywa się to ze względów praktycznych i ekonomicznych. Tę samą liczbę można wyrazić w różnych potęgach, dodając zera do mantysy przed lub po przecinku. Najłatwiej to zrozumieć, posługując się wykładnikiem dziesiętnym:
120 000 = 1,2×10 5 = 0,12×10 6 = 0,012×10 7 = 0,0012×10 8 itd. Natomiast wpisując stałą liczbę w nagłówku mantysy, za każdym razem otrzymamy nowe liczby. Przyjmijmy za pewnik, że przed naszymi 23 bitami będzie jeszcze jeden z jednym. Zwykle bit ten jest oddzielony od reszty kropką, co jednak nic nie znaczy. Tak jest po prostu wygodniej 1. 111000000000000000000000
Teraz wynikową mantysę należy podnieść do potęgi od lewej do prawej, zmniejszając moc o jeden z każdym krokiem. Zaczynamy od wartości potęgi, którą otrzymaliśmy w wyniku obliczeń, czyli
2 (celowo wybrałem prosty przykład, aby nie wpisywać każdej wartości potęgi dwójki i nie obliczałem ich w tabeli powyżej, gdy odpowiedni bit to zero)
1×2 2 + 1×2 1 + 1×2 0 + 1×2 -1 = 1×4 + 1×2 + 1×1 + 1×0,5 = 4+2+1+0,5 = 7.5 i otrzymałem wynik
7.5 , poprawność można sprawdzić np. pod
tym linkiem
Wyniki
Standardowa liczba zmiennoprzecinkowa
float
składa się z 32 bitów, pierwszy bit to znak (+ lub -), kolejnych osiem to wykładnik, kolejnych 23 to mantysa. Znak By - jeśli bit 0 jest liczbą dodatnią. Jeśli bit 1 jest ujemny.
Wykładniczo – zamieniamy bitowo na liczbę dziesiętną (pierwszy bit od lewej to
128 , drugi to
64 , trzeci to
32 , czwarty to
16 , piąty to
8 , szósty to
4 , siódmy to
2 , ósma to
1 ), od uzyskanej liczby odejmij
127 , otrzymamy stopień, od którego zaczniemy.
Zgodnie z mantysą - do istniejących 23 bitów z przodu dodajemy kolejny bit o wartości 1 i od niego zaczynamy podnosić do otrzymanej mocy, zmniejszając tę moc z każdym kolejnym bitem.
To wszystko, ludzie, dzieci! PS: W ramach pracy domowej, korzystając z tego artykułu, zostaw w komentarzach swoją wersję tego, dlaczego pojawiają się błędy precyzji w przypadku dużej liczby operacji arytmetycznych na liczbach zmiennoprzecinkowych
GO TO FULL VERSION