W jaki sposób 80-bitowy zmiennoprzecinkowy może być używany przez system 32-bitowy? [duplikować]

13

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”…

markzzz
źródło
8
W ten sam sposób robi 64-bitową liczbę zmiennoprzecinkową. Wykorzystuje 3 lub (2) 32-bitowe rejestry. Ponieważ 80-bitowa liczba zmiennoprzecinkowa nie jest nawet standardowym rozmiarem, w rzeczywistości byłaby to liczba 96-bitowa, przy użyciu tylko 80 bitów.
Ramhound
5
kiedy martwisz się o bicie platformy, martwisz się o wewnętrzne działanie procesora, jak powiedzieli inni, oraz o sposób, w jaki instrukcje są natywnie uruchamiane w systemie. Liczby IEEE754 są zwykle przetwarzane bezpośrednio w jednostkach wykonawczych FPU twojego CPU, podczas gdy liczba 128-bitowa wymagałaby użycia wielu zaprogramowanych instrukcji, aby mogły agregować znaczenie wartości, którą ocenia aplikacja. co pozostawia przetwarzanie numeru do wniosku.
Frank Thomas
2
@ Ƭᴇcʜιᴇ007 Nie całkiem dupe tego . To bardziej dotyczy różnicy między liczbą a jej reprezentacją tekstową / ASCII, chociaż niektóre z tych odpowiedzi mogą również rozwiązać ten problem.
Bob
3
W praktycznie wszystkich nowoczesnych maszynach zmiennoprzecinkowych jest obsługiwany przez osobny procesor (choć zwykle na tym samym układzie co procesor główny). Wszystkie takie procesory wiedzą, jak obsługiwać 80-bit (choć niektóre robią to znacznie szybciej niż inne). ( „Szerokość” procesora jest zresztą fikcją. )
Daniel R Hicks,
6
@Ramhound: Nie, 80 bitów jest cechą 8087 i wykorzystuje 1 rejestr FP. To zdecydowanie nie jest liczba 96 bitów.
MSalters

Odpowiedzi:

35

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.

LawrenceC
źródło
4
Wszystko, co dodałeś z mojej inspiracji, jest poprawne, więc nie martw się tam :) Należy wspomnieć o jednym punkcie, chociaż możesz użyć natywnych instrukcji procesora, w których istnieje jednostka zmiennoprzecinkowa, możesz również wykonywać operacje zmiennoprzecinkowe w oprogramowaniu (z równoważnym bitowym lub operacje matematyczne na liczbach całkowitych). Aby rozszerzyć do tego momentu, biorąc pod uwagę wystarczającą ilość pamięci, możesz również mieć dowolne liczby precyzji (w przeciwieństwie do naszych stałych wersji 64-bitowych lub 80-bitowych w tym przypadku) przy użyciu algorytmów / bibliotek programowych (jedną z najczęściej używanych jest GNU Multiple Precision biblioteka ).
Przełom
1
Nitpick: pierwszy zintegrowany układ FPU Intela był w 80486DX, a nie w 80386.
spudone
2
@markzzz: Nic nie jest szalone, jeśli jest wymagane. Używanie 16-bitowych pływaków do symulacji bomby atomowej w celu oceny zapasów nuklearnych jest szalone, ponieważ nie jest wystarczająco precyzyjne, abyś był pewny wyniku. W takim przypadku wymagane są 32 bity (i tak, dawniej te obliczenia były wykonywane na 16-bitowych urządzeniach PDP). Podobnie użycie 32-bitowych folatów do symulacji klimatu nie jest wystarczająco precyzyjne ze względu na chaotyczny charakter obliczeń.
slebetman
2
FWIW, powszechnym sposobem implementacji operacji zmiennoprzecinkowych na maszynach bez jednostek FP o pożądanym rozmiarze jest wykonanie tego w oprogramowaniu przy użyciu instrukcji liczb całkowitych. Ponieważ pod koniec dnia zarówno wykładnik, jak i mantysa liczby zmiennoprzecinkowej są jedynie liczbami całkowitymi. To, jak je interpretujemy, nadaje im szczególne znaczenie.
slebetman
2
@PTwr na x86 faktycznie masz 7 przydatnych GPR dla danych, w tym EBP. Po prostu ABI większości implementacji językowych rezerwuje ten rejestr na wskaźnik ramki stosu. Ale np. W GCC możesz użyć, -fomit-frame-pointeraby odzyskać ten rejestr.
Ruslan
13

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. ADCNa 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.

Przełom
źródło
2
Nie wspominasz o najważniejszym szczególe: FPU x87 na platformie x86 ma 80-bitowe rejestry zmiennoprzecinkowe, więc jego natywne obliczenia są w rzeczywistości wykonywane na 80-bitowych liczbach zmiennoprzecinkowych, bez potrzeby emulacji czegokolwiek w oprogramowaniu.
Matteo Italia,
@MatteoItalia Widzę to teraz, dziękuję. Myślałem, że pierwotne pytanie wymagało bardziej ogólnego przeglądu tego, jak można wykonywać operacje na liczbach większych niż rozmiar słowa procesora, a nie konkretnej implementacji rozszerzonych 80-bitowych liczb zmiennoprzecinkowych w x86 (również dlaczego mój przykład to 90 bitów zamiast 80 ...). Zaktualizowałem odpowiedź teraz, aby lepiej to odzwierciedlić, dziękuję za zgłoszenie się.
Przełom
5

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.

Floris
źródło
1
Byli powolni dopiero po pewnym momencie. Możesz pisać „szybkie” operacje zmiennoprzecinkowe, jeśli ograniczysz się do określonej specyfikacji.
Ramhound
3

„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!

Cort Ammon
źródło
Ale jeśli użyję 32-bitowych wartości zmiennoprzecinkowych w systemie 16-bitowym (lub 64-bitowych wartości zmiennoprzecinkowych w systemie 32-bitowym), będzie to wymagało tylko więcej pamięci (ponieważ musi się dwukrotnie zarejestrować)? Czy przetwarzanie informacji spowoduje dodatkowe obciążenie, więc zajmie to więcej czasu?
markzzz
@markzzz: Praca z wieloma rejestrami prawie zawsze zajmuje więcej czasu
Kaczka Mooing
Ładowanie 32-bitowej wartości zmiennoprzecinkowej do rejestrów specjalnego przeznaczenia zajmie więcej czasu. Jednak po zapisaniu ich jako 32-bitowe zmiennoprzecinkowe w rejestrach zmiennoprzecinkowych specjalnego przeznaczenia, sprzęt będzie działał na tych wartościach zmiennoprzecinkowych z pełną prędkością. Pamiętaj, że „16-bit” odnosi się tylko do wielkości rejestrów celu OGÓLNE. Rejestry zmiennoprzecinkowe są specjalnie dopasowane do ich zadań i mogą być szersze (w Twoim przypadku 32 bity)
Cort Ammon
2

„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.

ShadSterling
źródło