@n0name_l ma rację, że to przesunięcia bitowe, ale to nie jest takie proste jak by się wydawało.
Dla przesunięcia w lewo X << N
:
- Jeżeli X jest typem bez znaku (
unsigned
) taka operacja jest równoznaczna z (dla przepełnienia stosuje się odpowiednie reguły, wszystko jest zdefiniowane).
- Jeżeli X jest typem ze znakiem (
signed
) i jest nieujemne (≥ 0), taka operacja jest równoznaczna z jeżeli wynik ten mieści się w tym type - przepełnienie to undefined behavior.
- Jeżeli X jest typem ze znakiem (
signed
) i jest mniejsze od 0, taka operacja to undefined behavior.
Dla przesunięcia w prawo: X >> N
- Jeżeli X jest typem bez znaku (
unsigned
) albo typem ze znakiem (signed
) i jest nieujemne (≥ 0), taka operacja jest równoznaczna z (część ułamkowa jest obcinana).
- Jeżeli X jest typem ze znakiem (
signed
) i jest mniejsze od zera, wynik jest zdefiniowany przez implementację (implementation defined).
"Zdefiniowane przez implementację" w praktyce oznacza, że zostanie wykonane albo przesunięcie arytmetyczne albo logiczne (w prawo):
- Przesunięcie logiczne: przesunięcie bitów o
N
pozycji w prawo: brakujące pozycje są uzupełnione zerami, "nadmiarowe" bity znikają. Dla ujemnej liczby signed
oznacza to, że w zależności od zapisu staną się z nią dziwne rzeczy, ponieważ przesunięty zostanie też bit określający, że liczba jest ujemna.
- Przesunięcie arytmetyczne: jak przesunięcie logiczne, z tym, że najbardziej znaczący bit (MSB, bit znaku) jest po przesunięciu kopiowany z
X
. Oznacza to, że ujemna liczba dalej będzie ujemna, a operacja będzie równoznaczna z (część ułamkowa jest obcinana).
Wydaje mi się, że zazwyczaj wykonane zostanie przesunięcie arytmetyczne.
Dodam, że dla przesunięcia w lewo przesunięcie arytmetyczne i logiczne to to samo: przesunięcie bitów o N
pozycji w lewo tak, że brakujące pozycje są uzupełniane zerami, a te które "wypadają" są tracone.