Co się stanie, jeśli przypiszę wartość ujemną do zmiennej bez znaku?

82

Byłem ciekawy, co się stanie, jeśli przypiszę wartość ujemną do zmiennej bez znaku.

Kod będzie wyglądał mniej więcej tak.

unsigned int nVal = 0;
nVal = -5;

Nie dało mi to żadnego błędu kompilatora. Kiedy uruchomiłem program, nValprzypisano mu dziwną wartość! Czy to możliwe, że przypisywana jest wartość uzupełnienia jakiejś dwójki nVal?

ckv
źródło
1
Moje przeczucie (nie udało mi się go jeszcze znaleźć w standardzie) jest takie, że zachowanie jest technicznie niezdefiniowane. Co więcej, podejrzewam, że zobaczysz, czego się spodziewasz na prawie każdym kompilatorze, który znajdziesz. Więc chociaż zwykle widzisz takie zachowanie, prawdopodobnie nie jest dobrym pomysłem na to liczyć.
sblom
6
Nie jest nieokreślona (patrz § 4.7 / 2 ), ale reprezentacja (np. Uzupełnienie 2s) nie jest wymagana przez standard.
Georg Fritzsche
@gf (i in. poniżej), cool. Wygląda na to, że zachowanie jest w rzeczywistości wyraźnie zdefiniowane jako zgodne z oczekiwaniami, @viswanathan.
sblom
3
Druga linia jest odpowiednikiem nVal = (unsigned int) -5;. Obsada -5Do unsigned intjest zdefiniowany w 6.3.1.3. Reprezentacja w uzupełnieniu do 2s nie jest narzucona przez standard, ale algorytm konwersji na bez znaku to: „wartość jest konwertowana przez wielokrotne dodawanie lub odejmowanie o jeden więcej niż maksymalna wartość, która może być reprezentowana w nowym typie, dopóki wartość nie znajdzie się w zakresie nowego typu ”.
Pascal Cuoq
1
@Pascal: Wygląda na to, że odnosisz się do C99, ale pytanie jest oznaczone jako C ++.
Georg Fritzsche

Odpowiedzi:

67

Do oficjalnej odpowiedzi - sekcja 4.7 konw. Integralna

„Jeśli typ docelowy jest bez znaku, wynikowa wartość jest najmniejszą liczbą całkowitą bez znaku zgodną z liczbą całkowitą źródłową (modulo 2 n, gdzie njest liczbą bitów użytych do reprezentowania typu bez znaku). [Uwaga: W reprezentacji uzupełnienia do dwójki ta konwersja ma charakter koncepcyjny i nie ma zmiany w układzie bitowym (jeśli nie ma obcięcia). - koniec notatki]

Zasadniczo oznacza to, że jeśli podstawowa architektura przechowuje w metodzie, która nie jest dopełnieniem Two (np. Signed Magnitude lub One's Complement), to konwersja na unsigned musi zachowywać się tak, jakby była dopełnieniem Two.

Dennis Zickefoose
źródło
37
Co oznacza najmniejsza liczba całkowita bez znaku zgodna ze źródłową liczbą całkowitą ?
David Rodríguez - dribeas
11
@ DavidRodríguez-dribeas Jako przykład, 5 i 3 to "przystająca mod 2", ponieważ 5% 2 i 3% 2 to oba 1.
JoeQuery
Do jakich wersji standardu C ++ to się odnosi? Wszystko?
Alexey Kruglov
36

Przypisuje wzorzec bitowy reprezentujący -5 (w uzupełnieniu do 2) int bez znaku. Która będzie dużą wartością bez znaku. W przypadku 32-bitowych liczb całkowitych będzie to 2 ^ 32 - 5 lub 4294967291

Jasmeet
źródło
2
Bit nie ma z tym nic wspólnego.
GManNickG
1
@BenVoigt: W porządku, miałem na myśli, że nie ma to nic wspólnego z interpretacją bitów. (Oznacza to, że „bity” w cytowanej części są po prostu skrótem ceil(log_2(x)).)
GManNickG
1
@GManNickG Bit's (jak w, należy do bitu)? Komplement 2 (to bardzo miłe z twojej strony) ? GAAAAAAAAAAAAAH!
NullUserException
1
@NullUserException: Haha, wiem. Pisanie „*” zamiast tylko „* s” to straszny nawyk, który miałem od jakiegoś czasu. Jeśli chodzi o komplement zamiast komplementu, to po prostu głupota. :)
GManNickG
Prostota to podstawa. Ta odpowiedź ma to. (2 ^ 32-5) lepiej wyjaśnia to zachowanie niż cytowanie dokumentacji.
Redhart
4

Wyświetli się jako dodatnia liczba całkowita o wartości maksymalnej liczby całkowitej bez znaku - 4 (wartość zależy od architektury komputera i kompilatora).

BTW
Możesz to sprawdzić pisząc prosty program w C ++ typu „hello world” i przekonaj się sam

Dror Helper
źródło
Napisałem i sprawdziłem, dlatego zadałem pytanie, ale nie wiedziałem, w jaki sposób kompilator osiągnął tę dodatnią wartość. Dzięki
ckv
6
Niestety w C ++ pisanie programów do testowania zachowania nie zawsze jest dobrym pomysłem. Na przykład, jeśli ktoś spróbuje przetestować, co dzieje się w przypadku przepełnienia podpisanego , doprowadzi to do niezdefiniowanego zachowania, które nie gwarantuje, że będzie takie samo na każdym komputerze / kompilatorze.
Ben Jones,
4

Masz rację, liczba całkowita ze znakiem jest przechowywana w postaci uzupełnienia do 2, a liczba całkowita bez znaku jest przechowywana w reprezentacji binarnej bez znaku . C (i C ++) nie rozróżnia tych dwóch, więc wartość, którą otrzymujesz, jest po prostu wartością binarną bez znaku reprezentacji binarnej uzupełnienia do 2.

perimosocordiae
źródło
16
Nie można go przechowywać w komplementie 2.
GManNickG
Co to znaczy, że coś jest „przechowywane w dwójkach”? @GManNickG
JeremyF
4
@JeremyF: Nie "dwójki", "komplement dwójki". Jest to termin dostępny w Google i sposób przedstawiania liczb całkowitych ze znakiem.
GManNickG,
2

Tak, masz rację. Rzeczywista przypisana wartość jest podobna do wszystkich ustawionych bitów z wyjątkiem trzeciego. -1 to wszystkie ustawione bity (szesnastkowo: 0xFFFFFFFF), -2 to wszystkie bity oprócz pierwszego i tak dalej. To, co zobaczysz, to prawdopodobnie wartość szesnastkowa 0xFFFFFFFB, która dziesiętnie odpowiada 4294967291.

Jaskółka oknówka
źródło
3
Bity nie mają z tym nic wspólnego, reprezentacja liczb całkowitych nie jest określona.
GManNickG
2
Twoja odpowiedź jest poprawna, rygorystyczna, rzeczowa i coś, czego nigdy bym nie używał na zajęciach.
Martin
zobacz moją odpowiedź na uzupełnienie do 2 z -5. Nie sądzę, że poprawnie wykonałeś obliczenia matematyczne na wartościach binarnych.
cynistersix
0

Kiedy przypisujesz wartość ujemną do zmiennej bez znaku, używa ona metody dopełnienia 2 do jej przetworzenia iw tej metodzie zamienia wszystkie 0 na 1 i wszystkie 1 na 0, a następnie dodaje do niej 1. W twoim przypadku masz do czynienia z wartością int, która ma 4 bajty (32 bity), więc próbuje użyć metody uzupełniania 2 na liczbie 32-bitowej, co powoduje odwrócenie wyższego bitu. Na przykład:

┌─[student@pc]─[~]
└──╼ $pcalc 0y00000000000000000000000000000101      # 5 in binary
        5                       0x5                     0y101
┌─[student@pc]─[~]
└──╼ $pcalc 0y11111111111111111111111111111010      # flip all bits  
      4294967290      0xfffffffa      0y11111111111111111111111111111010
┌─[student@pc]─[~]
└──╼ $pcalc 0y11111111111111111111111111111010 + 1  # add 1 to that flipped binarry
      4294967291      0xfffffffb      0y11111111111111111111111111111011
Zafeer
źródło