Podpisane kontra liczby całkowite bez znaku

395

Czy słusznie mogę powiedzieć, że różnica między liczbą całkowitą ze znakiem i bez znaku jest następująca:

  1. Unsigned może zawierać większą wartość dodatnią i brak wartości ujemnej.
  2. Unsigned używa bitu wiodącego jako części wartości, podczas gdy wersja podpisana używa bitu najbardziej w lewo, aby określić, czy liczba jest dodatnia czy ujemna.
  3. liczby całkowite ze znakiem mogą zawierać liczby dodatnie i ujemne.

Jakieś inne różnice?

Shimmy Weitzhandler
źródło
6
Ponieważ 0 nie jest ani dodatnie, ani ujemne , bardziej odpowiednie jest użycie terminu wartość nieujemna zamiast wartości dodatniej dla liczb całkowitych bez znaku.
Daniel

Odpowiedzi:

344

Unsigned może zawierać większą wartość dodatnią i brak wartości ujemnej.

Tak.

Unsigned używa bitu wiodącego jako części wartości, podczas gdy wersja podpisana używa bitu najbardziej w lewo, aby określić, czy liczba jest dodatnia czy ujemna.

Istnieją różne sposoby reprezentowania podpisanych liczb całkowitych. Najłatwiejszym do zwizualizowania jest użycie lewego skrajnego bitu jako flagi ( znak i wielkość ), ale bardziej powszechne jest uzupełnienie dwóch . Oba są używane w większości współczesnych mikroprocesorów - zmiennoprzecinkowe używa znaku i wielkości, podczas gdy arytmetyka liczb całkowitych wykorzystuje uzupełnienie do dwóch.

liczby całkowite ze znakiem mogą zawierać liczby dodatnie i ujemne.

tak

Greg
źródło
Nie jestem pewien, czy to dokładnie ten tekst, ale znalazłem inny link. Przejdź do 9 strony pliku PDF (jest to właściwie 38 strona książki) i zobaczysz sekcję o nazwie Reprezentacja danych (sekcja 1.3). Zawiera wyjaśnienie wszystkich wyżej wymienionych rzeczy. lms.uop.edu.jo/lms/pluginfile.php/2420/mod_resource/content/1/…
WeirdElfB0y
92

Przejdę do różnic na poziomie sprzętowym na x86. Jest to w większości nieistotne, chyba że piszesz kompilator lub używasz języka asemblera. Ale miło to wiedzieć.

Po pierwsze, x86 ma natywne wsparcie dla reprezentacji uzupełnienia dwóch liczb podpisanych. Możesz użyć innych reprezentacji, ale wymagałoby to więcej instrukcji i generalnie marnowałoby czas procesora.

Co rozumiem przez „wsparcie rodzime”? Zasadniczo mam na myśli, że istnieje zestaw instrukcji, których używasz dla liczb niepodpisanych i inny zestaw, którego używasz dla liczb podpisanych. Numery niepodpisane mogą znajdować się w tych samych rejestrach, co numery podpisane, i rzeczywiście można łączyć instrukcje podpisane i niepodpisane bez martwienia się o procesor. Do kompilatora (lub programisty asemblera) należy śledzenie, czy numer jest podpisany, czy nie, i stosowanie odpowiednich instrukcji.

Po pierwsze, liczby uzupełniające dwóch mają tę właściwość, że dodawanie i odejmowanie jest takie samo jak w przypadku liczb niepodpisanych. Nie ma znaczenia, czy liczby są dodatnie czy ujemne. (Więc idź naprzód, ADDa SUBtwoje liczby bez obaw.)

Różnice zaczynają się ujawniać, jeśli chodzi o porównania. x86 ma prosty sposób ich rozróżnienia: powyżej / poniżej wskazuje porównanie bez znaku i większe / mniejsze niż wskazuje porównanie z podpisem. (Np. JAEOznacza „Skacz, jeśli jest wyżej lub równo” i jest niepodpisany.)

Istnieją również dwa zestawy instrukcji mnożenia i dzielenia, które dotyczą liczb całkowitych ze znakiem i bez znaku.

Na koniec: jeśli chcesz sprawdzić, powiedzmy, przepełnienie, zrobiłbyś to inaczej dla numerów podpisanych i niepodpisanych.

Artelius
źródło
Co rozumiesz przez liczby niepodpisane i podpisane, chcę zapytać, czy piszę niepodpisany int a = 2 i podpisany int b = 2, więc oba są podpisane lub niepodpisane, czy liczba podpisywana lub niepodpisana zależy od typu przypisujemy go lub zależy od tego, czy ma znak ujemny, czy nie? Martwi mnie to od dłuższego czasu.
Suraj Jain,
@SurajJain podpisane i niepodpisane odnoszą się do typów. Wskazują one, czy jest to możliwe dla zmienną lub wyrażenie ma wartość ujemną.
Artelius
Mam następujące wątpliwości, zadałem pytanie, nie ma jeszcze zadowalającej odpowiedzi, spójrz na to tutaj, stackoverflow.com/questions/41399092/...
Suraj Jain
62

Zapytał tylko o podpisane i niepodpisane. Nie wiem, dlaczego ludzie dodają do tego dodatkowe rzeczy. Pozwól, że powiem ci odpowiedź.

  1. Bez znaku: składa się tylko z wartości nieujemnych, tj. Od 0 do 255.

  2. Podpisano: składa się zarówno z wartości ujemnych, jak i dodatnich, ale w różnych formatach, takich jak

    • Od 0 do +127
    • -1 do -128

To wyjaśnienie dotyczy 8-bitowego systemu liczbowego.

Ashish Kumar
źródło
17

Tylko kilka punktów za kompletność:

  • ta odpowiedź dotyczy tylko reprezentacji liczb całkowitych. Mogą być inne odpowiedzi na zmiennoprzecinkowe;

  • reprezentacja liczby ujemnej może się różnić. Najczęstszym (jak dotąd - dziś jest prawie uniwersalny) w użyciu jest obecnie uzupełnienie dwóch . Inne reprezentacje obejmują uzupełnienie (dość rzadkie) i oznaczoną wielkość (znikająco rzadkie - prawdopodobnie używane tylko w muzeach), która po prostu wykorzystuje wysoki bit jako wskaźnik znaku, a pozostałe bity reprezentują bezwzględną wartość liczby.

  • Podczas korzystania z uzupełnienia do dwóch zmienna może reprezentować większy zakres (o jeden) liczb ujemnych niż liczb dodatnich. Jest tak, ponieważ zero jest uwzględniane w liczbach „dodatnich” (ponieważ bit znaku nie jest ustawiony na zero), ale nie są liczbami ujemnymi. Oznacza to, że nie można przedstawić wartości bezwzględnej najmniejszej liczby ujemnej.

  • kiedy używasz swojego uzupełnienia lub wielkości ze znakiem, możesz mieć zero reprezentowane jako liczbę dodatnią lub ujemną (co jest jednym z kilku powodów, dla których te reprezentacje nie są zwykle używane).

Michael Burr
źródło
Jeśli napiszę niepodpisany int a = -2 i podpisany int b = -2, czy podstawowa reprezentacja byłaby taka sama, wiem, że nie jest dobrze, aby liczba bez znaku miała wartość ujemną, ale jeśli ją podam, to co podstawowa reprezentacja?
Suraj Jain,
1
Minig niggle: znak i wielkość są używane w zmiennoprzecinkowym IEEE, więc jest to dość powszechne. :-)
alastair
14

Zgodnie z tym, czego nauczyliśmy się w klasie, liczby całkowite ze znakiem mogą reprezentować zarówno liczby dodatnie, jak i ujemne, podczas gdy liczby całkowite bez znaku są tylko nieujemne.

Na przykład patrząc na liczbę 8-bitową :

niepodpisane wartości 0do255

podpisane wartości mieszczą się w zakresie od -128do127

Ying Xiong
źródło
11

Wszystko oprócz punktu 2 jest poprawne. Istnieje wiele różnych oznaczeń dla podpisanych int, niektóre implementacje używają pierwszego, inne używają ostatniego, a jeszcze inne używają czegoś zupełnie innego. Wszystko zależy od platformy, z którą pracujesz.

Jasper Bekkers
źródło
Czy to jest coś małego i dużego?
vIceBerg
little vs. big endian ma związek z kolejnością bajtów na platformie. Mały endian może zrobić 0xFF 0xFE 0x7F, podczas gdy duży endian zrobi 0x7F 0xFE 0xFF.
Jasper Bekkers
10

Kolejną różnicą jest konwersja liczb całkowitych o różnych rozmiarach.

Na przykład, jeśli wyodrębniasz liczbę całkowitą ze strumienia bajtów (dla uproszczenia powiedzmy 16 bitów), z wartościami bez znaku, możesz:

i = ((int) b[j]) << 8 | b[j+1]

(prawdopodobnie powinien rzucić 2 nd bajt, ale zgaduję kompilator zrobi dobry uczynek)

W przypadku podpisanych wartości należy się martwić o rozszerzenie znaku i wykonać:

i = (((int) b[i]) & 0xFF) << 8 | ((int) b[i+1]) & 0xFF
Mike Gleen
źródło
5

Ogólnie rzecz biorąc, jest to poprawne. Nie wiedząc nic więcej o tym, dlaczego szukasz różnic, nie mogę wymyślić żadnych innych różnic między podpisanym a niepodpisanym.

Toddk
źródło
4

Oprócz tego, co powiedzieli inni, w C nie można przepełnić liczby całkowitej bez znaku; zachowanie jest zdefiniowane jako arytmetyka modułu. Możesz przepełnić podpisaną liczbę całkowitą i teoretycznie (choć nie w praktyce w obecnych systemach głównego nurtu) przepełnienie może spowodować błąd (być może podobny do błędu dzielenia przez zero).

Jonathan Leffler
źródło
1
Zauważ, że przepełnienie liczb całkowitych ze znakiem wywołuje niezdefiniowane zachowanie, a współczesne kompilatory są bardzo agresywne w wykrywaniu tego i wykorzystywaniu go do modyfikowania programu w nieoczekiwany, ale technicznie uzasadniony sposób, ponieważ mogą zakładać, że nieokreślone zachowanie nie nastąpi - z grubsza. To o wiele bardziej problem niż 7 lat temu.
Jonathan Leffler
4
  1. Tak, liczba całkowita bez znaku może przechowywać dużą wartość.
  2. Nie, istnieją różne sposoby wyświetlania wartości dodatnich i ujemnych.
  3. Tak, liczba całkowita ze znakiem może zawierać zarówno wartości dodatnie, jak i ujemne.
bhavesh
źródło
4

(w odpowiedzi na drugie pytanie) Używając tylko bitu znakowego (a nie uzupełnienia 2), możesz otrzymać -0. Niezbyt ładna.

Ryan Rodemoyer
źródło
Aby dodać do tej odpowiedzi, w zasadzie oznacza to, że 10 == 00, gdy obie te liczby są podstawą 2.
4

Liczby całkowite ze znakiem w C reprezentują liczby. Jeśli ai bsą zmiennymi typów całkowitych ze znakiem, standard nigdy nie będzie wymagał od kompilatora przekształcenia wyrażenia a+=bw anic innego niż sumę arytmetyczną ich odpowiednich wartości. Oczywiście, jeśli suma arytmetyczna nie byłaby dopasowana a, procesor może nie być w stanie jej tam umieścić, ale standard nie wymagałby od kompilatora obcinania lub zawijania wartości, ani robienia czegokolwiek innego w tym zakresie, jeśli wartości przekraczające limity dla ich typów. Należy zauważyć, że chociaż standard tego nie wymaga, implementacje języka C mogą przechwytywać przepełnienia arytmetyczne o podpisanych wartościach.

Niespisane liczby całkowite w C zachowują się jak abstrakcyjne algebraiczne pierścienie liczb całkowitych, które są zgodne z pewną potęgą dwóch, z wyjątkiem scenariuszy obejmujących konwersje na większe typy lub operacje z nimi. Konwersja liczby całkowitej o dowolnym rozmiarze na 32-bitowy typ bez znaku da element członkowski odpowiadający rzeczom, które są zgodne z tym modem całkowitym 4,294,967,296. Powodem odjęcia 3 od 2 daje 4 294 967 295, że dodanie czegoś zgodnego do 3 do czegoś zgodnego do 4 294 967 295 da coś zgodnego do 2.

Abstrakcyjne typy pierścieni algebraicznych to często przydatne rzeczy; niestety C używa sygnatury jako decydującego czynnika decydującego o tym, czy typ powinien zachowywać się jak pierścień. Co gorsza, niepodpisane wartości są traktowane jako liczby, a nie pierścieniowe elementy, gdy są konwertowane na większe typy, a niepodpisane wartości mniejsze niż intsą konwertowane na liczby, gdy wykonywana jest na nich jakaś arytmetyka. Jeśli vjest uint32_trówny 4,294,967,294, to v*=v;powinien zrobić v=4. Niestety, jeśli intma 64 bity, nie wiadomo, co v*=v;można zrobić.

Biorąc pod uwagę obecny standard, sugerowałbym używanie typów niepodpisanych w sytuacjach, w których chce się zachowania związanego z pierścieniami algebraicznymi, a typów podpisanych, gdy chcemy reprezentować liczby. Szkoda, że ​​C narysował rozróżnienia w ten sposób, ale są tacy, jacy są.

supercat
źródło
3

Niezapisane liczby całkowite są znacznie bardziej prawdopodobne, że złapią Cię w określonej pułapce niż liczby całkowite ze znakiem. Pułapka wynika z faktu, że chociaż powyższe 1 i 3 są poprawne, obu typom liczb całkowitych można przypisać wartość poza granicami tego, co może „zatrzymać” i zostanie ona po cichu przeliczona.

unsigned int ui = -1;
signed int si = -1;

if (ui < 0) {
    printf("unsigned < 0\n");
}
if (si < 0) {
    printf("signed < 0\n");
}
if (ui == si) {
    printf("%d == %d\n", ui, si);
    printf("%ud == %ud\n", ui, si);
}

Po uruchomieniu tego otrzymasz następujące dane wyjściowe, mimo że obie wartości zostały przypisane do -1 i zadeklarowane inaczej.

signed < 0
-1 == -1
4294967295d == 4294967295d
Mateusz
źródło
0

Jedyną gwarantowaną różnicą między wartością ze znakiem i bez znaku w C jest to, że wartość ze znakiem może być ujemna, 0 lub dodatnia, podczas gdy bez znaku może być tylko 0 lub dodatnia. Problem polega na tym, że C nie definiuje formatu typów (więc nie wiesz, że twoje liczby całkowite są w uzupełnieniu do dwóch). Ściśle mówiąc, pierwsze dwa wymienione przez ciebie punkty są nieprawidłowe.

Jaśniejsze
źródło
0

Musisz używać liczb całkowitych bez znaku podczas programowania w systemach wbudowanych. W pętlach, gdy nie ma potrzeby stosowania liczb całkowitych ze znakiem, użycie liczb całkowitych bez znaku pozwoli zaoszczędzić bezpieczeństwo niezbędne do zaprojektowania takich systemów.

Fahad Naeem
źródło