2009-07-24 10 views
5

Jest to wątpliwość co do reprezentacji bitów liczb całkowitych ze znakiem. Na przykład, jeśli chcesz reprezentować -1, jest to równoważne uzupełnieniu 2 (+1). Więc -1 jest reprezentowane jako 0xFFFFFFF. Teraz, kiedy przesuwam liczbę o 31 i drukuję wynik, wraca on jako -1.Liczba bitów reprezentujących liczby ujemne

signed int a = -1; 
printf(("The number is %d ",(a>>31));//this prints as -1 

Czy ktoś może mi wyjaśnić, w jaki sposób bity są reprezentowane dla liczb ujemnych?

Dzięki.

+0

Patrząc na to z innej strony: myśl o dowolnej zmianie N-bitowej (wydłużającej znak) jako dzielącej przez 2^N, zaokrąglając W DÓŁ (w kierunku minus nieskończoności, a nie w kierunku 0.) Stąd -1 przesunięte w prawo (ze znakiem rozszerzenie) dowolną liczbę razy będzie produkować -1. – vladr

Odpowiedz

10

Gdy górny bit wynosi zero, liczba jest dodatnia. Kiedy jest 1, liczba jest ujemna.

Liczby ujemne przesunięte w prawo, przesuwają "1" jako najwyższy bit, aby numer był ujemny. Właśnie dlatego dostajesz tę odpowiedź.

Aby uzyskać więcej informacji o uzupełnieniu do dwóch, zobacz this Stackoverflow question.


@Stobor zwraca uwagę, że niektóre implementacje C mogła przesunąć 0 do wysokiej bit zamiast 1. [zweryfikowana w Wikipedii.] W języku Java to niezawodnie arytmetyczna shift.

Ale wynik podany przez pytającego pokazuje, że jego kompilator wykonuje przesunięcie arytmetyczne.

+0

Ale y shud to drukowane jako -1, powinno wydrukować jako 1 rytuał od góry, ale jest 1, który reprezentuje liczbę ujemną. –

+1

@Nosredna: powinieneś również wspomnieć, że zachowanie z bitem fill-with-sign jest specyficzne dla implementacji. – Stobor

+0

Ponieważ reprezentuje liczbę ujemną, dlaczego miałaby drukować liczbę dodatnią? – Nosredna

1

Jest to operacja arytmetyczna, która zachowuje bit znaku i przesuwa mantysową część podpisanego numeru.

okrzyki

8

Standard C pozostawia nieokreślone czy prawy shift negatywnej (koniecznie podpisane) całkowitą przesuwa zera (przesunięcie bitowe w prawo) lub Zaloguj bitów (arytmetyka przesunięcia w prawo) do najbardziej znaczącego bitu. To do wyboru należy wybór.

W związku z tym kod przenośny zapewnia, że ​​nie wykonuje prawidłowych przesunięć na liczbach ujemnych. Konwertuje wartość na odpowiednią wartość bez znaku przed zmianą (co gwarantuje użycie logicznego przesunięcia w prawo, wstawianie zer do opuszczonych bitów) lub zapewnia, że ​​wartość jest dodatnia lub toleruje zmienność wyniku.

+0

Albo może "lub" w górnym bicie (lub bitach) po zmianie. – Nosredna

+0

Bardzo dziękuję za wszystkie odpowiedzi. Nie jestem w stanie jeszcze wyczyścić mojego doyubtu. Czy to jest kompilator czy specyfikacja sprzętowa ??? –

+2

@Maddy: zależy to od sprzętu i kompilatora.Na przykład, jeśli konkretny procesor ma tylko logiczne przesunięcie w prawo (LSR), a nie arytmetyczne przesunięcie w prawo (ASR), kompilatory dla tego komputera będą używać LSR, a nie nieistniejącego ASR. Zależy to również od kompilatora; nawet jeśli maszyna ma zarówno ASR, jak i LSR, program piszący kompilator może zdecydować o użyciu LSR zarówno dla ilości podpisanych, jak i niepodpisanych, z wielu powodów. Może być szybciej; może być zgodny z innymi platformami, na których działa ten sam kompilator; być może osoba pisząca kompilator nie lubi ASR. –

1

Zasadniczo istnieją dwa rodzaje przesunięcia w prawo. Niepodpisane przesunięcie w prawo i podpisane przesunięcie w prawo. Niepodpisane przesunięcie w prawo przesunie bity w prawo, powodując utratę najmniej znaczącego bitu, a najbardziej znaczący bit zostanie zastąpiony przez 0. Z podpisaną prawą zmianą bity są przesunięte w prawo, powodując najmniejszy błąd. znaczący bit będzie zagubiony i najważniejszy bit do zachowania. Znane przesunięcie w prawo dzieli liczbę przez potęgę dwóch (odpowiadającą liczbie przesuniętych miejsc), podczas gdy przesunięcie niepodpisane jest logiczną operacją przesuwania.

Operator ">>" wykonuje niepodpisane przesunięcie w prawo, gdy typ danych, na którym działa, jest niepodpisany i wykonuje podpisane przesunięcie w prawo, gdy typ danych, na którym działa, jest podpisywany. Tak więc, musisz wykonać rzut obiektu na liczbę całkowitą bez znaku, zanim wykonasz manipulację bitową, aby uzyskać pożądany wynik.

+0

Niekoniecznie "podpisana zmiana" dla liczb całkowitych ze znakiem (chociaż jest to bardzo często wykonywane w ten sposób) - zależy to od kompilatora i/lub sprzętu. –

+0

Tak. Zgadza się. Już od tak dawna programowałem na dobrze zachowanych komputerach laptop/komputery stacjonarne/serwerowe z niektórymi wariantami x86 i kompilując z GCC, że często łatwo jest zapomnieć o różnicy między tym, jakie założenia są uzasadnione dla obecnej aplikacji, a tym, co standard faktycznie ma. gwarancje. –

0

EDIT: Gdy poniżej został napisany kod w pytaniu została zapisana jako:

unsigned int a = -1; 
printf(("The number is %d ",(a>>31));//this prints as -1 

Jeśli unsigned int jest co najmniej 32 bitów szerokości, wówczas kompilator jest naprawdę nie wolno produkować -1 jako wynik tego (z małym zastrzeżeniem, że powinieneś rzutować niepodpisaną wartość na int przed przejściem do printf).

Ponieważ a jest unsigned int, przypisanie -1 do niego musi nadać mu wartość UINT_MAX (jako najmniejsza nieujemna wartość zgodna z -1 modulo UINT_MAX + 1). Dopóki unsigned int ma co najmniej 32 bity na twojej platformie, wynikiem przesunięcia niepodpisanej liczby o 31 będzie UINT_MAX podzielony przez 2^31, który musi zmieścić się w int. (Jeśli unsigned int ma 31 bitów lub mniej, może wygenerować wszystko, co mu się podoba, ponieważ wynik przesunięcia jest nieokreślony).

Powiązane problemy