Ponieważ system 32-bitowy nie może zarządzać liczbą 2 ^ 33 (ponieważ oczywisty limit 32-bitowy), w jaki sposób można zarządzać 80-bitową liczbą zmiennoprzecinkową ?
Powinien wymagać „80-bit”…
Ponieważ system 32-bitowy nie może zarządzać liczbą 2 ^ 33 (ponieważ oczywisty limit 32-bitowy), w jaki sposób można zarządzać 80-bitową liczbą zmiennoprzecinkową ?
Powinien wymagać „80-bit”…
Odpowiedzi:
Jednym ze znaczeń 32-bitowego procesora jest to, że jego rejestry mają szerokość 32 bitów. Nie oznacza to, że nie może poradzić sobie z liczbami 64-bitowymi, tylko że najpierw musi poradzić sobie z dolną 32-bitową połową, a następnie z górną 32-bitową połową sekundy. (Właśnie dlatego procesory mają flagę przenoszenia .) Jest wolniejszy niż, jeśli procesor mógłby po prostu załadować wartości do szerszego 64-bitowego rejestru, ale nadal jest to możliwe.
Zatem „bitowość” systemu niekoniecznie ogranicza rozmiar liczb, z którymi program może sobie poradzić, ponieważ zawsze można rozbić operacje, które nie mieszczą się w rejestrach procesora, na wiele operacji. Spowalnia więc operacje, zużywa więcej pamięci (jeśli musisz użyć pamięci jako „notatnika”) i trudniej ją programować, ale operacje są nadal możliwe.
Jednak nic z tego nie ma znaczenia, na przykład, dla 32-bitowych procesorów Intel i liczb zmiennoprzecinkowych, ponieważ zmiennoprzecinkowa część procesora ma własne rejestry i mają one szerokość 80 bitów. (Na początku historii x86 funkcja zmiennoprzecinkowa była osobnym układem, była zintegrowana z procesorem od 80486DX.)
Odpowiedź @ Breakthrough zainspirowała mnie do dodania tego.
Wartości zmiennoprzecinkowe, o ile są przechowywane w rejestrach FPU, działają zupełnie inaczej niż binarne wartości całkowite.
80 bitów wartości zmiennoprzecinkowej jest dzielonych między mantysę i wykładnik potęgi (istnieje również „podstawa” w liczbach zmiennoprzecinkowych, która zawsze wynosi 2). Mantysa zawiera cyfry znaczące, a wykładnik określa, jak duże są te cyfry znaczące. Więc nie ma „przelewu” do innego rejestru, jeśli twoja liczba staje się zbyt duża, aby zmieścić się w mantysie, twój wykładnik rośnie i tracisz precyzję - tzn. Kiedy zamienisz ją na liczbę całkowitą, stracisz miejsca po przecinku z prawej strony - dlatego nazywa się to zmiennoprzecinkowe.
Jeśli wykładnik jest zbyt duży, wówczas występuje przepełnienie zmiennoprzecinkowe, ale nie można go łatwo rozszerzyć na inny rejestr, ponieważ wykładnik i mantysa są ze sobą powiązane.
Mógłbym być trochę nieprecyzyjny i mylić się co do niektórych z tego, ale wierzę, że to sedno tego. (Ten artykuł w Wikipedii ilustruje to nieco bardziej zwięźle.)
Jest OK, że działa to zupełnie inaczej, ponieważ cała „zmiennoprzecinkowa” część procesora jest jakby w swoim własnym świecie - do uzyskania dostępu do niego używasz specjalnych instrukcji procesora. Ponadto, jeśli chodzi o kwestię pytania, ponieważ jest osobna, bitowość FPU nie jest ściśle powiązana z bitami natywnego procesora.
-fomit-frame-pointer
aby odzyskać ten rejestr.
Wszystkie wersje 32-bitowe, 64-bitowe i 128-bitowe odnoszą się do długości słowa procesora, którą można uznać za „podstawowy typ danych”. Często jest to liczba bitów przesłanych do / z pamięci RAM systemu i szerokość wskaźników (chociaż nic nie powstrzymuje Cię przed użyciem oprogramowania w celu uzyskania dostępu do większej ilości pamięci RAM niż dostęp do pojedynczego wskaźnika).
Zakładając stałą szybkość zegara (a także wszystko inne w architekturze są stałe) i zakładając, że odczyt / zapis pamięci jest taki sam (zakładamy tutaj 1 cykl zegara, ale w rzeczywistości jest to dalekie od rzeczywistego), możesz dodaj dwie 64-bitowe liczby w jednym cyklu zegara na komputerze 64-bitowym (trzy, jeśli policzysz pobieranie liczb z pamięci RAM):
ADDA [NUM1], [NUM2]
STAA [RESULT]
Możemy również wykonać to samo obliczenie na maszynie 32-bitowej ... Jednak na maszynie 32-bitowej musimy to zrobić w oprogramowaniu, ponieważ najpierw należy dodać niższe 32-bity, skompensować przepełnienie, a następnie dodać górne 64 bity:
ADDA [NUM1_LOWER], [NUM2_LOWER]
STAA [RESULT_LOWER]
CLRA ; I'm assuming the condition flags are not modified by this.
BRNO CMPS ; Branch to CMPS if there was no overflow.
ADDA #1 ; If there was overflow, compensate the value of A.
CMPS ADDA [NUM1_UPPER], [NUM2_UPPER]
STAA [RESULT_UPPER]
Przeglądając moją złożoną składnię, możesz łatwo zobaczyć, w jaki sposób operacje o wyższej precyzji mogą zajmować wykładniczo dłuższy czas na maszynie o mniejszej długości słów. To jest prawdziwy klucz do procesorów 64-bitowych i 128-bitowych: pozwalają nam obsługiwać większą liczbę bitów w jednej operacji. Niektóre maszyny zawierają instrukcje dodawania innych ilości z przeniesieniem (np. ADC
Na x86), ale powyższy przykład ma na myśli dowolne wartości precyzji.
Teraz, aby rozszerzyć to na pytanie, łatwo jest zobaczyć, jak moglibyśmy dodać liczby większe niż dostępne rejestry - po prostu dzielimy problem na części wielkości rejestrów i pracujemy od tego miejsca. Chociaż, jak wspomniano w @MatteoItalia , stos FPU x87 ma natywną obsługę dla wielkości 80-bitowych, w systemach pozbawionych tego wsparcia (lub procesorów całkowicie pozbawionych jednostki zmiennoprzecinkowej!), Równoważne obliczenia / operacje muszą być wykonane w oprogramowaniu .
Tak więc dla liczby 80-bitowej po dodaniu każdego segmentu 32-bitowego można również sprawdzić przepełnienie do 81-go bitu i opcjonalnie wyzerować bity wyższego rzędu. Te kontrole / zera są wykonywane automatycznie dla niektórych instrukcji x86 i x86-64, w których podane są rozmiary operandu źródłowego i docelowego (chociaż są one określone tylko w potęgach 2, zaczynając od szerokości 1 bajta).
Oczywiście przy liczbach zmiennoprzecinkowych nie można po prostu wykonać dodawania binarnego, ponieważ mantysa i cyfry znaczące są spakowane razem w formie przesunięcia. W jednostce ALU procesora x86 znajduje się obwód sprzętowy do wykonania tego dla pływaków 32-bitowych i 64-bitowych IEEE; jednak nawet przy braku jednostki zmiennoprzecinkowej (FPU), te same obliczenia mogą być wykonywane w oprogramowaniu (np. za pomocą Biblioteki Naukowej GNU , która używa FPU po kompilacji na architekturze, wracając do algorytmów oprogramowania jeśli nie jest dostępny sprzęt zmiennoprzecinkowy [np. dla wbudowanych mikrokontrolerów bez FPU]).
Biorąc pod uwagę wystarczającą ilość pamięci, można również wykonać obliczenia na podstawie liczby dowolnych (lub „nieskończonych” - w granicach realistycznych) precyzji, wykorzystując więcej pamięci, ponieważ wymagana jest większa precyzja. Jedna z tych implementacji istnieje w bibliotece GNU Multiple Precision , co pozwala na nieograniczoną precyzję (oczywiście do momentu zapełnienia pamięci RAM) operacji na liczbach całkowitych, wymiernych i zmiennoprzecinkowych.
Architektura pamięci systemu może umożliwiać przenoszenie tylko 32 bitów jednocześnie - ale to nie powstrzymuje go przed użyciem większych liczb.
Pomyśl o pomnożeniu. Możesz znać swoje tabliczki mnożenia do 10x10, ale prawdopodobnie nie masz problemu z wykonaniem 123x321 na kartce papieru: po prostu rozbijasz go na wiele małych problemów, mnożąc poszczególne cyfry i dbając o przenoszenie itp.
Procesory mogą zrobić to samo. W „dawnych czasach” miałeś 8-bitowe procesory, które mogły wykonywać obliczenia zmiennoprzecinkowe. Ale były powolne.
„32-bit” to tak naprawdę sposób kategoryzowania procesorów, a nie ustalenie reguły. „32-bitowy” procesor zazwyczaj ma 32-bitowe rejestry ogólnego przeznaczenia do pracy.
Jednak nie ma żadnych wymagań, aby wszystko w procesorze odbywało się w wersji 32-bitowej. Na przykład nie było niespotykane, aby komputer „32-bitowy” miał 28-bitową magistralę adresową, ponieważ wytwarzanie sprzętu było tańsze. Komputery 64-bitowe często mają 40-bitową lub 48-bitową szynę pamięci z tego samego powodu.
Arytmetyka zmiennoprzecinkowa to kolejne miejsce, w którym rozmiary się różnią. Wiele 32-bitowych procesorów obsługuje 64-bitowe liczby zmiennoprzecinkowe. Dokonali tego, przechowując wartości zmiennoprzecinkowe w specjalnych rejestrach, które były szersze niż rejestry ogólnego przeznaczenia. Aby zapisać jedną z tych dużych liczb zmiennoprzecinkowych w rejestrach specjalnych, należy najpierw podzielić liczbę na dwa rejestry ogólnego przeznaczenia, a następnie wydać instrukcję łączenia ich w liczbę zmiennoprzecinkową w rejestrach specjalnych. W rejestrach zmiennoprzecinkowych wartości byłyby zmieniane jako zmiennoprzecinkowe 64-bitowe, a nie jako para 32-bitowych połówek.
Wspomniana 80-bitowa arytmetyka jest tego szczególnym przypadkiem. Jeśli pracujesz z liczbami zmiennoprzecinkowymi, znasz niedokładność wynikającą z problemów z zaokrąglaniem liczb zmiennoprzecinkowych. Jednym z rozwiązań dla zaokrąglania jest posiadanie większej ilości precyzji, ale następnie musisz przechowywać większe liczby i zmuszać programistów do używania w pamięci niezwykle dużych wartości zmiennoprzecinkowych.
Rozwiązanie firmy Intel polega na tym, że wszystkie rejestry zmiennoprzecinkowe mają 80 bitów, ale instrukcje przenoszenia wartości do / z tych rejestrów działają przede wszystkim na liczbach 64-bitowych. Dopóki działasz całkowicie w ramach zmiennoprzecinkowego stosu Intela x87, wszystkie twoje operacje są wykonywane z 80 bitami precyzji. Jeśli twój kod musi wyciągnąć jedną z tych wartości z rejestrów zmiennoprzecinkowych i zapisać ją gdzieś, obcina ją do 64-bitów.
Morał tej historii: kategorie, takie jak „32-bit”, są zawsze bardziej ryzykowne, gdy zagłębisz się w rzeczy!
„32-bitowy” procesor to taki, w którym większość rejestrów danych to rejestry 32-bitowe, a większość instrukcji działa na danych w tych rejestrach 32-bitowych. 32-bitowy procesor może również przesyłać dane do iz pamięci 32-bitowej jednocześnie. Większość rejestrów w wersji 32-bitowej nie oznacza, że wszystkie rejestry są 32-bitowe. Krótka odpowiedź jest taka, że 32-bitowy procesor może mieć pewne funkcje, które wykorzystują inne liczby bitów, takie jak 80-bitowe rejestry zmiennoprzecinkowe i odpowiednie instrukcje.
Jak powiedział @spudone w komentarzu do odpowiedzi @ ultrasawblade, pierwszym procesorem x86, który miał zintegrowane operacje zmiennoprzecinkowe, był Intel i486 (konkretnie 80486DX, ale nie 80486SX), który zgodnie ze stroną 15-1 programistów mikroprocesorów i486 Podręcznik odniesienia zawiera w swoich rejestrach numerycznych „Osiem 80-bitowych rejestrów numerycznych indywidualnie adresowanych”. I486 ma 32-bitową magistralę pamięci, więc przesłanie wartości 80-bitowej zajęłoby 3 operacje pamięci.
Poprzednik generacji 486, i386, nie miał żadnych zintegrowanych operacji zmiennoprzecinkowych. Zamiast tego miał obsługę zewnętrznego „zmiennoprzecinkowego” zmiennoprzecinkowego, 80387. Ten koprocesor miał prawie taką samą funkcjonalność jak zintegrowany z i486, jak można zobaczyć na stronie 2-1 80387 Podręcznik programisty .
80-bitowy format zmiennoprzecinkowy wydaje się pochodzić z 8087, koprocesora matematycznego dla 8086 i 8088. 8086 i 8088 były 16-bitowymi procesorami (z 16-bitowymi i 8-bitowymi szynami pamięci) i nadal były w stanie użyć 80-bitowego formatu zmiennoprzecinkowego, korzystając z 80-bitowych rejestrów w koprocesorze.