Numery podpisane i niepodpisane

17

W jaki sposób ALU w mikroprocesorze rozróżnia podpisany numer -7 oznaczony jako 1111 i niepodpisany numer 15, oznaczony również przez 1111?

noorav
źródło
3
Zobacz odpowiedź na powiązane pytanie: cs.stackexchange.com/a/30047/28999 . Nawiasem mówiąc, podpisany -7 nie jest reprezentowany jako 1111. To -1. Następnie np. 1111 - 0001 = 1110 zarówno w przypadku podpisanym, jak i niepodpisanym (-2 vs 14)
Albert Hendriks
2
@AlbertHendriks Uczciwie, niektóre starsze komputery używają „reprezentacji wielkości znaku” (jeden bit znaku i bity wielkości ), a my nadal używamy tego stylu, na przykład dla liczb zmiennoprzecinkowych IEEE. Są po prostu nieeleganckie i ciężko z nimi pracować w porównaniu z uzupełnieniem do dwóch. n1
Draconis,
1
Główna różnica polega na tym, jak zachowują się operatorzy Po pomnożeniu i podzieleniu wynik jest taki sam.
Rob
2
@Rob To nie do końca poprawne. Dodawanie, odejmowanie i mnożenie są takie same między znakiem bez znaku a dopełnieniem dwójki - zakładając, że twoje dane wejściowe i wyjściowe są tego samego rozmiaru. Podział nie jest taki sam 6/2 to 3, ale -2/2 to -1. I wiele procesorów ma instrukcje mnożenia, w których dwa wejścia mają identyczny rozmiar, ale wyjście jest dwa razy większe, w takim przypadku niepodpisane i uzupełnienie dwójki również nie są takie same.
kasperd

Odpowiedzi:

14

Krótka i prosta odpowiedź brzmi: nie. Żaden współczesny główny procesor ISA nie działa tak, jak myślisz.

W przypadku procesora jest to tylko trochę wzorzec. To od Ciebie, programisty, zależy, co oznacza ten wzór bitowy.

Ogólnie rzecz biorąc, ISA nie rozróżniają różnych typów danych, jeśli chodzi o przechowywanie. (Ignorowanie rejestrów specjalnego przeznaczenia, takich jak rejestry zmiennoprzecinkowe w FPU.) To tylko bezsensowny wzorzec bitów do CPU. Jednak MSRF zrobić mają różne rodzaje instrukcji, które mogą interpretować wzorzec bitowy na różne sposoby. Na przykład, instrukcje arytmetyczne, takie jak MUL, DIV, ADD, SUBinterpretuje wzór bitowy jako pewnego rodzaju numeru, natomiast instrukcji logicznych takich jak AND, OR, XORinterpretować je jako tablica wartości logiczne. Tak więc to od programisty (lub autora interpretera lub kompilatora, jeśli używasz języka wyższego poziomu), zależy, czy wybierzesz prawidłowe instrukcje.

Na przykład mogą istnieć osobne instrukcje dla liczb podpisanych i niepodpisanych. Niektóre ISA mają również instrukcje arytmetyczne z cyframi dziesiętnymi kodowanymi binarnie.

Zauważ jednak, że napisałem powyżej „współczesny główny nurt ISA”. W rzeczywistości istnieją niezależne lub historyczne ISA, które działają inaczej. Na przykład zarówno oryginalny 48-bitowy ISA CISC IBM AS / 400, jak i obecny oparty na POWER 64-bitowy ISA RISC systemu o nazwie teraz IBM i, rozróżniają wskaźniki i inne wartości. Wskaźniki są zawsze oznaczone i zawierają informacje o typie oraz zarządzanie prawami. Procesor wie, czy wartość jest wskaźnikiem, czy nie, i tylko uprzywilejowane jądro systemu i / OS może dowolnie manipulować wskaźnikami. Aplikacje użytkownika mogą manipulować posiadanymi przez siebie wskaźnikami w celu wskazania pamięci, którą posiadają, za pomocą niewielkiej liczby bezpiecznych instrukcji.

Było też kilka historycznych projektów ISA, które obejmowały przynajmniej pewną ograniczoną formę rozpoznawania typu.

Jörg W Mittag
źródło
Pamiętaj, że kod bajtowy Java również liczy się jako ISA. I w zasadzie zależy na typach danych ...
John Dvorak,
Kod bajtowy Java jest w pewnym sensie liczony jako ISA, w tym sensie, że został zaimplementowany w krzemie. Jednak tego rodzaju sprawdzanie typu podstawowego jest sprawdzane przez moduł ładujący klasy, więc typy można w większości zignorować w czasie wykonywania. I oczywiście kod bajtowy Java nie ma niepodpisanych typów.
pseudonim
@Pseudonym: Dobrze, technicznie, to nie ma char, który jest 16-bitowy typ unsigned. Oczywiście nadal nie ma niepodpisanych instrukcji arytmetycznych w kodzie bajtowym Java, ponieważ wszelkie charwartości są automatycznie promowane do int(arytmetyka 32-bitowa).
Ilmari Karonen,
42

Krótka wersja: nie wiadomo. Nie można tego powiedzieć.

Jeśli 1111reprezentuje -7, to masz reprezentację wielkości znaku , gdzie pierwszy bit jest znakiem, a pozostałe bity są wielkością. W takim przypadku arytmetyka jest nieco skomplikowana, ponieważ dodawanie niepodpisane i dodawanie podpisane używają innej logiki. Tak że prawdopodobnie mają SADDa UADDkod operacji, a jeśli wybierzesz niewłaściwy masz nonsensownych wyników.

Częściej jednak 1111reprezentuje -1 w tak zwanej reprezentacji uzupełnienia do dwóch . W tym przypadku ALU po prostu nie dba o to, czy numery są podpisane, czy niepodpisane! Na przykład weźmy operację 1110 + 0001. W arytmetyce ze znakiem oznacza to „-2 + 1”, a wynik powinien wynosić -1 ( 1111). W arytmetyce bez znaku oznacza to „14 + 1”, a wynik powinien wynosić 15 ( 1111). ALU nie wie więc, czy chcesz podpisany, czy niepodpisany wynik, i nie obchodzi go to. Po prostu dodaje to tak, jakby było niepodpisane, a jeśli chcesz później traktować to jako liczbę całkowitą ze znakiem, to zależy od ciebie.

EDYCJA: Jak słusznie wskazują Ruslan i Daniel Schepler w komentarzach, niektóre operandy wciąż wymagają osobnych podpisanych i niepodpisanych wersji, nawet na maszynie z dwójką. Dodawanie, odejmowanie, mnożenie, równość i takie wszystko działa dobrze, nie wiedząc, czy liczby są podpisane, czy nie. Ale podział i wszelkie porównania większe niż / mniejsze niż muszą mieć osobne wersje.

EDYCJA EDYCJA: Istnieją również inne reprezentacje, takie jak uzupełnienie , ale w zasadzie nigdy nie są one używane, więc nie powinieneś się o nie martwić.

Draconis
źródło
Ach, gotcha. Dzięki za to :)
noorav,
10
W reprezentacji uzupełnienia dwóch trzy operacje arytmetyczne są niezależne od podpisania: dodawanie, odejmowanie i mnożenie (z iloczynem tej samej długości co argumenty). Jedynie podział musi być traktowany inaczej dla podpisanych operandów.
Ruslan
4
Istnieje również porównanie: < <= >= >są różne dla operandów podpisanych i niepodpisanych, ==a jednocześnie !=są niezależne od podpisania .
Daniel Schepler
Mnożenie często ma podpisane i niepodpisane odmiany: 0xFFFFFFFF * 0xFFFFFFFF to 0xFFFFFFFE00000001, jeśli nie podpisano, i 0x0000000000000001, jeśli podpisano. Procesory takie jak Intel zwracają wynik w 2 rejestrach, a najwyższy rejestr różni się dla podpisanego i niepodpisanego. Rejestr dolny to 1 w obu sytuacjach.
Rudy Velthuis,
9

Jedną z wielkich zalet matematyki z uzupełnieniem do dwóch, z której korzystają wszystkie współczesne architektury, jest to, że instrukcje dodawania i odejmowania są dokładnie takie same dla operandów podpisanych i niepodpisanych.

Wiele procesorów nie ma nawet instrukcji mnożenia, dzielenia lub modułu. Jeśli tak, muszą mieć osobne podpisane i niepodpisane formularze instrukcji, a kompilator (lub programista w asemblerze) wybierze odpowiednią.

Procesory mają również generalnie różne instrukcje dotyczące podpisanych lub niepodpisanych porównań. Na przykład x86 może postępować zgodnie ze CMPz JL(skok jeśli mniej niż) jeżeli porównanie powinna zostać podpisana, lub JB(Przełącz jeśli poniżej) jeżeli porównanie powinno być podpisane. Znów kompilator lub programista wybrałby odpowiednią instrukcję dla typu danych.

Kilka innych instrukcji często występuje w podpisanych i niepodpisanych wariantach, takich jak przesunięcie w prawo lub załadowanie wartości do szerszego rejestru, z rozszerzeniem znaku lub bez.

Davislor
źródło
1
Nawet mnożenie jest takie samo dla liczb całkowitych bez znaku i ze znakiem (uzupełnienie do dwóch), pod warunkiem, że wynik nie ma więcej bitów niż danych wejściowych . Jeśli robisz coś w rodzaju mnożenia 8 × 8 → 16 bitów (lub 16 × 16 → 32 bitów itp.), Musisz podpisać przedłużyć wejścia (lub wyniki pośrednie) .
Ilmari Karonen,
@IlmariKaronen To prawda; ARM A32 / A64 są zestawami instrukcji, które mają wiele form instrukcji mnożenia, w tym mnożące dodawanie, niezależnie od znaku, które zapisuje tylko bity niższego rzędu, ale także smulhi umulhktóre zwracają tylko górne bity mnożenia oraz instrukcje podpisane i niepodpisane, które zwróć wynik w rejestrze dwa razy szerszym niż operandy źródłowe.
Davislor
6

Tak nie jest. Procesor polega na zestawie instrukcji, aby powiedzieć mu, jakiego rodzaju danych szuka i gdzie je wysłać. Nic w zerach i zerach w samym operandzie nie może z natury sygnalizować ALU, czy dane to char, float, int, sign int itp. Jeśli ten 1111 idzie do obwodu elektrycznego, który oczekuje uzupełnienia 2s, to idzie należy interpretować jako uzupełnienie 2s.

Jay Speidell
źródło
Nie ma czegoś takiego jak charna poziomie sprzętowym. Może kiedyś, w czasach mechanicznych drukarek. Ale dzisiaj charto tylko liczba, jeśli chodzi o sprzęt. Powodem, dla którego różne liczby odpowiadają różnym kształtom liter na ekranie, jest to, że te liczby są używane do wybierania różnych map bitowych lub różnych procedur rysowania z dużego stołu (tj. Z „czcionki”).
Solomon Slow
3

Chciałbym dodać dodatek do już udzielonych odpowiedzi:

W większości innych odpowiedzi zauważono, że w arytmetyki dopełniającej dwójki wynik jest taki sam dla liczb podpisanych i niepodpisanych:

-2 + 1 = -1     1110 + 0001 = 1111
14 + 1 = 15     1110 + 0001 = 1111

Istnieją jednak wyjątki:

Division:
  -2 / 2 = -1     1110 / 0010 = 1111
  14 / 2 = 7      1110 / 0010 = 0111
Comparison:
  -2 < 2 = TRUE   1110 < 0010 = TRUE
  14 < 2 = FALSE  1110 < 0010 = FALSE
"Typical" (*) multiplication:
  -2 * 2 = -4     1110 * 0010 = 11111100
  14 * 2 = 28     1110 * 0010 = 00011100

(*) Na wielu procesorach wynik mnożenia dwóch liczb n-bitowych ma szerokość (2 * n) bitów.

Do takich operacji procesory mają różne instrukcje dotyczące arytmetyki podpisanej i niepodpisanej.

Oznacza to, że programista (lub kompilator) musi użyć innych instrukcji dla arytmetyki podpisanej i niepodpisanej.

Na przykład procesor x86 ma instrukcję nazwaną divdo wykonania niepodpisanego podziału i instrukcję nazwaną idivdo wykonania podpisanego podziału.

Istnieją również różne instrukcje „warunkowe” (skoki warunkowe, set-bit-on-condition), a także instrukcje mnożenia dla arytmetyki podpisanej i niepodpisanej.

Martin Rosenau
źródło