przesunięcia bitowe są czasami uważane za operacje bitowe, ponieważ traktują wartość jako serię bitów, a nie jako ilość liczbową. W tych operacjach cyfry są przesuwane lub przesuwane w lewo lub w prawo. Rejestry w procesorze komputerowym mają stałą szerokość, więc niektóre bity będą” przesunięte „z rejestru na jednym końcu, podczas gdy ta sama liczba bitów będzie „przesunięta” z drugiego końca; różnice między operatorami przesunięcia bitów polegają na tym, jak określają wartości przesuniętych bitów.
adresowanie Bitówedit
jeśli szerokość rejestru (często 32 lub nawet 64) jest większa niż liczba bitów (Zwykle 8) najmniejszej jednostki adresowalnej (elementu atomowego), często nazywanej bajtem, operacje przesunięcia wywołują schemat adresowania na bitach.Pomijając efekty brzegowe na obu końcach rejestru, operacje przesunięcia arytmetycznego i logicznego zachowują się tak samo, a przesunięcie o 8 pozycji bitowych przenosi wzorzec bitowy o 1 pozycję bajtową w następujący sposób:
|
przesunięcie w lewo o 8 pozycji zwiększa adres bajtowy o 1 |
|
przesunięcie w prawo o 8 pozycji zmniejsza adres bajtowy o 1 |
|
przesunięcie w lewo o 8 pozycji zmniejsza adres bajtowy o 1 |
|
przesunięcie w prawo o 8 pozycji zwiększa adres bajtowy o 1 |
zmiana Arytmetycznaedytuj
w przesunięciu arytmetycznym bity przesunięte poza oba końce są odrzucane. W lewym przesunięciu arytmetycznym zera są przesuwane po prawej stronie; w prawym przesunięciu arytmetycznym bit znaku (MSB w dopełnieniu dwójki) jest przesunięty po lewej stronie, zachowując w ten sposób znak argumentu.
ten przykład wykorzystuje rejestr 8-bitowy, interpretowany jako dopełnienie dwóch:
00010111 (decimal +23) LEFT-SHIFT= 00101110 (decimal +46)
10010111 (decimal −105) RIGHT-SHIFT= 11001011 (decimal −53)
w pierwszym przypadku, lewa cyfra została przesunięta za koniec rejestru, a nowa cyfra 0 została przesunięta w prawą pozycję. W drugim przypadku przesunięto skrajną prawą jedynkę (być może w stronę flagi nośnej), a nową jedynkę skopiowano w skrajną lewą pozycję, zachowując znak liczby. Wielokrotne zmiany są czasami skracane do pojedynczej zmiany o pewną liczbę cyfr. Na przykład:
00010111 (decimal +23) LEFT-SHIFT-BY-TWO= 01011100 (decimal +92)
przesunięcie arytmetyczne w lewo przez n jest równoważne pomnożeniu przez 2N (pod warunkiem, że wartość nie przepełnia), podczas gdy przesunięcie arytmetyczne w prawo przez N wartości dopełniacza dwójki jest równoważne dzieleniu przez 2N i zaokrąglaniu w kierunku ujemnej nieskończoności. Jeśli liczba binarna jest traktowana jako dopełnienie jedynki, to ta sama operacja przesunięcia w prawo powoduje dzielenie przez 2n i zaokrąglenie w kierunku zera.
przesunięcie logiczne w lewo
|
przesunięcie logiczne w prawo
|
w logicznym przesunięciu zera są przesuwane w celu zastąpienia odrzuconych bitów. Dlatego logiczne i arytmetyczne przesunięcia w lewo są dokładnie takie same.
jednak, ponieważ logiczne przesunięcie w prawo wstawia wartość 0 bitów do najbardziej znaczącego bitu, zamiast kopiowania bitu znaku, jest idealne dla niepodpisanych liczb binarnych, podczas gdy arytmetyczne przesunięcie w prawo jest idealne dla liczb binarnych dopełnienia dwóch znaków.
Circular shiftEdit
inną formą przesunięcia jest przesunięcie kołowe, obrót bitowy lub obrót bitowy.
RotateEdit
przesunięcie lub obrót w lewo
|
przesunięcie lub obrót w prawo
|
w tej operacji, czasami nazywanej rotate no carry, bity są „obracane” tak, jakby lewy i prawy koniec rejestru był połączony. Wartość przesunięta w prawo podczas przesunięcia w lewo jest dowolną wartością przesuniętą w lewo, oraz odwrotnie dla operacji przesunięcia w prawo. Jest to przydatne, jeśli konieczne jest zachowanie wszystkich istniejących bitów i jest często używane w kryptografii cyfrowej.
Rotate through carryedytuj
Lewy obrót przez przenoszenie
|
prawy obrót poprzez przenoszenie
|
Rotate through carry jest wariantem operacji rotate, gdzie bit, który jest przesunięty (na obu końcach) jest starą wartością flagi carry, a bit, który jest przesunięty (na drugim końcu) staje się nową wartością flagi carry.
pojedynczy obrót poprzez przenoszenie może symulować logiczne lub arytmetyczne przesunięcie o jedną pozycję, ustawiając wcześniej flagę przenoszenia. Na przykład, jeśli flaga carry zawiera 0, to x RIGHT-ROTATE-THROUGH-CARRY-BY-ONE
jest logicznym przesunięciem w prawo, a jeśli flaga carry zawiera kopię bitu znaku, to x RIGHT-ROTATE-THROUGH-CARRY-BY-ONE
jest arytmetycznym przesunięciem w prawo. Z tego powodu niektóre mikrokontrolery, takie jak low end PICs, po prostu obracają się i obracają poprzez przenoszenie, i nie przejmują się instrukcjami arytmetycznymi lub logicznymi przesunięciami.
Rotate through carry jest szczególnie przydatny podczas wykonywania przesunięć na liczbach większych niż natywny Rozmiar słowa procesora, ponieważ jeśli duża liczba jest przechowywana w dwóch rejestrach, bit przesunięty z jednego końca pierwszego rejestru musi wejść na drugi koniec drugiego. Dzięki rotate-through-carry bit ten jest „zapisywany” we fladze carry podczas pierwszej zmiany, gotowy do zmiany podczas drugiej zmiany bez dodatkowego przygotowania.
w językach wysokiego poziomuedytuj
c-familyEdit
w językach rodziny C operatorami logicznymi są „<<
” dla left shift i „>>
” dla right shift. Liczba miejsc do przesunięcia jest podana jako drugi argument dla operatora. Na przykład,
x = y << 2;
przypisuje x
wynik przesunięcia y
w lewo o dwa bity, co jest równoważne mnożeniu przez cztery.
zmiany mogą skutkować zachowaniem zdefiniowanym przez implementację lub niezdefiniowanym zachowaniem, dlatego należy zachować ostrożność podczas ich używania. Wynikiem przesunięcia o liczbę bitów większą lub równą wielkości słowa jest niezdefiniowane zachowanie w C i C++. Przesunięcie w prawo wartości ujemnej jest zdefiniowane w implementacji i nie jest zalecane przez dobrą praktykę kodowania; wynik przesunięcia w lewo wartości podpisanej jest niezdefiniowany, jeśli wynik nie może być reprezentowany w typie wyniku.
w C# przesunięcie w prawo jest przesunięciem arytmetycznym, gdy pierwszy argument jest int lub long. Jeśli pierwszy argument jest typu uint lub ulong, przesunięcie w prawo jest przesunięciem logicznym.
przesunięcia Kołowe
rodzina języków C nie posiada operatora obrotu, ale można go zsyntetyzować z operatorów przesunięcia. Należy zadbać o to, aby oświadczenie było dobrze uformowane, aby uniknąć nieokreślonych zachowań i ataków czasowych w oprogramowaniu z wymaganiami bezpieczeństwa. Na przykład naiwna implementacja, która w lewo obraca 32-bitową niepodpisaną wartość x
o n
pozycje, jest po prostu:
uint32_t x = ..., n = ...;uint32_t y = (x << n) | (x >> (32 - n));
przesunięcie o 0
bitów skutkuje jednak niezdefiniowanym zachowaniem w wyrażeniu po prawej stronie (x >> (32 - n))
, ponieważ 32 - 0
to 32
, a 32
jest poza zakresem włącznie. Druga próba może skutkować:
uint32_t x = ..., n = ...;uint32_t y = n ? (x << n) | (x >> (32 - n)) : x;
gdzie kwota przesunięcia jest testowana, aby upewnić się, że nie wprowadza niezdefiniowanego zachowania. Jednak gałąź dodaje dodatkową ścieżkę kodu i stwarza możliwość analizy czasu i ataku, co często jest niedopuszczalne w oprogramowaniu o wysokiej integralności. Ponadto kod kompiluje się do wielu instrukcji maszynowych, co często jest mniej wydajne niż natywne instrukcje procesora.
aby uniknąć niezdefiniowanego zachowania i gałęzi pod GCC i Clang, zaleca się następujące kroki. Wzorzec jest rozpoznawany przez wiele kompilatorów, a kompilator będzie emitował jedną instrukcję rotate:
uint32_t x = ..., n = ...;uint32_t y = (x << n) | (x >> (-n & 31));
istnieją również specyficzne dla kompilatora elementy implementujące przesunięcia kołowe, takie jak _rotl8, _rotl16, _rotr8, _rotr16 w Microsoft Visual C++. Clang zapewnia pewne elementy wewnętrzne rotate dla zgodności z Microsoft, które cierpią na powyższe problemy. GCC nie oferuje funkcji rotate intrinsics. Intel dostarcza również Intel X86.
JavaEdit
w Javie wszystkie typy liczb całkowitych są podpisywane, więc operatory „<<
” i „>>
” wykonują przesunięcia arytmetyczne. Java dodaje operator ” >>>
„do wykonywania logicznych przesunięć w prawo, ale ponieważ logiczne i arytmetyczne operacje przesunięcia w lewo są identyczne dla liczby całkowitej ze znakiem, w Javie nie ma operatora” <<<
„.
więcej szczegółów o operatorach Java shift:
- operatory
<<
(lewy shift),>>
(podpisany prawy shift) i>>>
(niepodpisany prawy shift) są nazywane operatorami zmiany. - typ wyrażenia przesunięcia jest promowanym typem lewego argumentu. Na przykład
aByte >>> 2
jest odpowiednikiem((int) aByte) >>> 2
. - jeśli promowanym typem lewego argumentu jest Int, jako odległość przesunięcia używane jest tylko pięć bitów najniższego rzędu prawego argumentu. To tak, jakby prawostronny operand został poddany bitowemu operatorowi logicznemu i & o wartości maski 0x1f (0b11111). Faktycznie zastosowana odległość przesunięcia mieści się więc zawsze w zakresie od 0 do 31 włącznie.
- jeśli promowany Typ lewego argumentu jest długi, to tylko sześć bitów najniższego rzędu prawego argumentu jest używanych jako odległość przesunięcia. To tak, jakby prawostronny operand został poddany bitowemu operatorowi logicznemu i & o wartości maski 0x3f (0b111111). Faktycznie zastosowana odległość przesunięcia mieści się więc zawsze w zakresie od 0 do 63 włącznie.
- wartość
n >>> s
to N przesuniętych w prawo pozycji bitowych s z zerowym rozszerzeniem. - w operacjach bitowych i przesunięć Typ
byte
jest domyślnie konwertowany naint
. Jeśli wartość bajtu jest ujemna, najwyższy bit to jeden, wtedy te są używane do wypełnienia dodatkowych bajtów w int. Tak więcbajt b1 = -5; int i = b1 | 0x0200;
spowodujei == -5
.
JavaScriptEdit
JavaScript używa operacji bitowych do obliczenia każdej z dwóch lub więcej jednostek na 1 lub 0.
PascalEdit
w języku Pascal, jak również we wszystkich jego dialektach (takich jak Object Pascal i Standard Pascal), logiczne operatory przesunięcia w lewo i w prawo to odpowiednio „shl
” i ” shr
„. Nawet dla liczb całkowitych ze znakiem shr
zachowuje się jak logiczne przesunięcie i nie kopiuje bitu znaku. Liczba miejsc do przesunięcia jest podana jako drugi argument. Na przykład, następujące przypisanie x wynik przesunięcia y w lewo o dwa bity:
x := y shl 2;