Czy przesunięcie bitowe zależy od endianizmu?

156

Przypuśćmy, że mam numer 'numb'=1025 [00000000 00000000 00000100 00000001] reprezentowaną :

Na maszynie Little-Endian:

00000001 00000100 00000000 00000000

Na maszynie Big-Endian:

00000000 00000000 00000100 00000001

Teraz, jeśli zastosuję lewy Shift na 10 bitach (tj .: numb << = 10), powinienem mieć:

[A] Na maszynie Little-Endian:

Jak zauważyłem w GDB, Little Endian wykonuje lewe przesunięcie w 3 krokach: [Pokazałem "3" kroki, aby lepiej zrozumieć tylko przetwarzanie]

  1. Traktuj nie. w konwencji Big-Endian:

    00000000        00000000        00000100    00000001
  2. Zastosuj lewy Shift:

    00000000        00010000        00000100        00000000
  3. Przedstaw wynik ponownie w Little-Endian:

    00000000        00000100        00010000        00000000 

[B]. Na maszynie Big-Endian:

00000000        00010000        00000100        00000000

Moje pytanie brzmi:

Jeśli bezpośrednio zastosuję Przesunięcie w Lewo w Konwencji Little Endian, powinno to dać:

numb:

00000001 00000100 00000000 00000000

numb << 10:

00010000 00000000 00000000 00000000

Ale w rzeczywistości daje:

00000000        00000100        00010000        00000000 

Aby osiągnąć tylko drugi wynik, powyżej pokazałem trzy hipotetyczne kroki.

Proszę wyjaśnić, dlaczego powyższe dwa wyniki są różne: Rzeczywisty wynik numb << 10jest inny niż oczekiwany.

Sandeep Singh
źródło

Odpowiedzi:

194

Endianizm to sposób, w jaki wartości są przechowywane w pamięci. Po załadowaniu do procesora, niezależnie od endianness, instrukcja przesunięcia bitu działa na wartości w rejestrze procesora. Dlatego ładowanie z pamięci do procesora jest równoznaczne z konwersją do formatu big endian, następna jest operacja przesuwania, a następnie nowa wartość jest ponownie zapisywana w pamięci, w której ponownie zaczyna obowiązywać kolejność bajtów little endian.

Aktualizacja, dzięki @jww: Na PowerPC przesunięcia i obroty wektora są wrażliwe na endian. Możesz mieć wartość w rejestrze wektorowym, a przesunięcie da różne wyniki na little-endian i big-endian .

Carl
źródło
4
Dziękuję za wyjaśnienie. Czy możesz zasugerować jakieś odniesienie, w którym mogę lepiej zrozumieć takie zawiłości.
Sandeep Singh
4
Najlepszą rzeczą do zrozumienia endianizmu jest rzeczywiste użycie go na różnych architekturach na poziomie osadzonym. Mógłbym jednak odesłać Cię do tych dwóch artykułów: codeproject.com/KB/cpp/endianness.aspx i ibm.com/developerworks/aix/library/au-endianc/ ...
Carl
3
Więc mój kod będzie działał niezależnie od endian ?! to jest świetne! Tak się martwiłem, że będę musiał zhakować mój kod do piekła iz powrotem!
MarcusJ
2
@MarcusJ: Niekoniecznie. Na przykład, jeśli odczytujesz 4 bajty z pliku, który reprezentuje 32-bitową liczbę całkowitą, musisz wziąć pod uwagę endianness odczytywanych danych w połączeniu z endianness systemu otrzymującego dane, aby poprawnie zinterpretować dane.
Carl
3
W PowerPC wektor przesuwa się i obraca jest wrażliwy na endian. Możesz mieć wartość w rejestrze wektorowym, a przesunięcie da różne wyniki na little-endian i big-endian.
jww
58

Nie, przesunięcie bitowe, jak każda inna część C, jest definiowane w kategoriach wartości , a nie reprezentacji. Przesunięcie w lewo o 1 to mnożenie o 2, przesunięcie w prawo to dzielenie. (Jak zawsze w przypadku operacji bitowych, uważaj na podpis. Wszystko jest najlepiej zdefiniowane dla typów całkowitych bez znaku).

Kerrek SB
źródło
1
Jest to zasadniczo prawdziwe dla arytmetyki liczb całkowitych, ale C zapewnia wiele przypadków zachowania zależnego od reprezentacji.
Edmund,
2
@Edmund: Hm ... przede wszystkim nie określono implementacji podpisu, aw konsekwencji zachowanie operacji bitowych (takich jak przesunięcie w prawo) oraz modulo i divide jest implementacją zdefiniowaną na ujemnych liczbach całkowitych. Jakie inne rzeczy masz na myśli, które są zdefiniowane w ramach implementacji?
Kerrek SB,
@KerrekSB niestety nie są one implementacją zdefiniowaną na ujemnych liczbach całkowitych. Są nieokreślone w C89 i niezdefiniowane w C99 +, co było bardzo złym pomysłem.
Paolo Bonzini
@PaoloBonzini: Tak, słuszna uwaga. W rzeczywistości jest to nawet lepsze, ponieważ wzmacnia punkt, w którym operacje przesunięcia są definiowane w kategoriach wartości, być może są niezdefiniowane, gdy wynik nie jest reprezentowalny, a spekulowanie na temat podstawowej reprezentacji nie pomaga.
Kerrek SB
@KerrekSB: chodzi o to, że tak naprawdę każdy potrzebuje przesunięcia w lewo, aby być reprezentowanym zarówno jako wartości, jak i jako reprezentacja, w zależności od przypadku. Używanie liczb całkowitych bez znaku może powodować inne problemy, na przykład x &= -1u << 20najprawdopodobniej będzie niepoprawne, jeśli xjest 64-bitowe i int32-bitowe. Z tego powodu GCC obiecuje nigdy nie traktować podpisanych zmian jako nieokreślonych lub nawet nieokreślonych.
Paolo Bonzini
5

Każda instrukcja przesunięcia najpierw przesuwa bity wyższego rzędu, jest uważana za przesunięcie w lewo. Każda instrukcja przesunięcia najpierw przesuwa bity niższego rzędu, uważana jest za przesunięcie w prawo. W tym sensie zachowanie liczb >>i <<dla unsignedliczb nie będzie zależało od endianizmu.

Davislor
źródło
4

Komputery nie zapisują liczb tak, jak my. Wartość po prostu się zmienia. Jeśli nalegasz, aby patrzeć na to bajt po bajcie (nawet jeśli komputer to nie robi), możesz powiedzieć, że na komputerze little-endian pierwszy bajt przesuwa się w lewo, nadmiarowe bity przechodzą do drugiego bajtu, i tak dalej.

(Nawiasem mówiąc, little-endian ma więcej sensu, jeśli zapiszesz bajty pionowo, a nie poziomo, z wyższymi adresami na górze. Tak się składa, że ​​często rysuje się diagramy map pamięci).

Raymond Chen
źródło
2

Chociaż przyjęta odpowiedź wskazuje, że endianess jest pojęciem z punktu widzenia pamięci. Ale nie sądzę, aby to odpowiadało bezpośrednio na pytanie.

Niektóre odpowiedzi mówią mi, że operacje bitowe nie zależą od endianess , a procesor może reprezentować bajty w inny sposób. W każdym razie, mówi się o tym, że endianess zostaje wyabstrahowany.

Ale kiedy wykonujemy na przykład obliczenia bitowe na papierze, czy nie musimy w pierwszej kolejności podawać endianess? W większości przypadków wybieramy endianinę w sposób pośredni.

Na przykład załóżmy, że mamy taki wiersz kodu

0x1F & 0xEF

Jak obliczysz wynik ręcznie, na papierze?

  MSB   0001 1111  LSB
        1110 1111
result: 0000 1111

Więc tutaj używamy formatu Big Endian do wykonania obliczeń. Możesz także użyć Little Endian, aby obliczyć i uzyskać ten sam wynik.

Przy okazji, kiedy piszemy liczby w kodzie, myślę, że jest to format Big Endian. 123456lub0x1F większość znaczących liczb zaczyna się od lewej strony.

Ponownie, gdy tylko napiszemy jakiś format binarny wartości na papierze, myślę, że już wybraliśmy Endianess i oglądamy wartość tak, jak widzimy ją z pamięci.

Wracając do pytania, operację przesunięcia <<należy traktować jako przejście z LSB (najmniej znaczący bajt) do MSB (najbardziej znaczący bajt) .

W takim razie jak na przykład w pytaniu:

numb=1025

Little Endian

LSB 00000001 00000100 00000000 00000000 MSB

Więc << 10byłoby 10bitprzejście od LSB do MSB.


Porównanie i << 10operacje dla formatu Little Endian krok po kroku:

MSB                                        LSB
    00000000  00000000  00000100  00000001  numb(1025)
    00000000  00010000  00000100  00000000  << 10

LSB                                        MSB
    00000000  00000100  00010000  00000000 numb(1025) << 10, and put in a Little Endian Format

LSB                                        MSB
    00000001  00000100  00000000  00000000 numb(1205) in Little Endian format
    00000010  00001000  00000000  00000000 << 1 
    00000100  00010000  00000000  00000000 << 2 
    00001000  00100000  00000000  00000000 << 3 
    00010000  01000000  00000000  00000000 << 4
    00100000  10000000  00000000  00000000 << 5
    01000000  00000000  00000001  00000000 << 6
    10000000  00000000  00000010  00000000 << 7
    00000000  00000001  00000100  00000000 << 8
    00000000  00000010  00001000  00000000 << 9
    00000000  00000100  00010000  00000000 << 10 (check this final result!)

Łał! Otrzymuję oczekiwany wynik zgodnie z opisem PO!

Problemy, których PO nie przyniosła oczekiwanego rezultatu, są następujące:

  1. Wygląda na to, że nie przeszedł z LSB na MSB.

  2. Przesuwając bity w formacie Little Endian, powinieneś zdać sobie sprawę (dzięki Bogu, zdaję sobie z tego sprawę), że:

LSB 10000000 00000000 MSB << 1jest
LSB 00000000 00000001 MSB, nie LSB 01000000 00000000 MSB

Ponieważ dla każdej osoby 8bitsfaktycznie piszemy to w plikuMSB 00000000 LSB Big Endian.

Więc to jest jak

LSB[ (MSB 10000000 LSB) (MSB 00000000 LSB) ]MSB


Podsumowując:

  1. Chociaż mówi się, że operacje bitowe są wyodrębniane z blablablabla ..., kiedy ręcznie obliczamy operacje bitowe, nadal musimy wiedzieć, jakiego endianess używamy, gdy zapisujemy format binarny na papierze. Musimy również upewnić się, że wszyscy operatorzy używają tej samej endianess.

  2. OP nie uzyskał oczekiwanego rezultatu, ponieważ źle wykonał zmianę biegów.

Stóg
źródło