비트 시프트 연산자란 말그대로 bit 를 shift 시켜주는 녀석이다.

연산자는 다음과 같이 3가지가 있다.

(1) << : 왼쪽으로 이동

(2) >> : 오른쪽으로 이동

(3) >>> : unsigned, 오른쪽으로 이동


좀 더 쉽게 이해해보자.

'A << B' 라는 수식이 있다고 하면, A 라는 녀석의 비트 값을 B 만큼 왼쪽으로 이동시키겠다.

라고 이해하면 된다. (단, A와 B는 정수이다)



이제 하나씩 알아보자.


(1) << 연산자

int a = 0x00000010;    // 10진수 '16' 을 16진수로 표현

a = a << 2;


연산 前, a = 0000 0000 0000 0000 0000 0000 0001 0000    // 16

연산 後, a = 0000 0000 0000 0000 0000 0000 0100 0000    // 64


색상으로 표시된 부분에 주목하자.

비트 연산을 할 때는 비트가 rotation 되는 게 아니라 

왼쪽 끝부분이 그냥 잘리고 없어진다. C언어도 마찬가지이다.


음수의 경우도 살펴보겠다. 

(참고로 int 는 32 비트지만 맨 앞의 비트는 부호 역할을 하므로 - 양수는 0, 음수는 1 -

 최소값은 -2^31, 최대값은 2^31-1 이다)


a = -2147483648    // -2^31

a <<= 1;


연산 前, a = 1000 0000 0000 0000 0000 0000 0000 0000    // -2147483648 = -2^31

연산 後, a = 0000 0000 0000 0000 0000 0000 0000 0000    // 0


<< 연산자를 정리하자면,

비트 값들을 왼쪽으로 이동시킨 후에 오른쪽의 빈 공간에는 모두 0 으로 채운다.



(2) >> 연산자

int a = 2147483647;    // 2^31-1

a >>= 1;


연산 前, a = 0111 1111 1111 1111 1111 1111 1111 1111    // 2147483647 = 2^31-1

연산 後, a = 0011 1111 1111 1111 1111 1111 1111 1111    // 1073741823 = 2^30-1


이번엔 오른쪽 끝부분이 잘리고 없어진다.

하지만 << 연산자와 똑같다고 생각하면 오산이다.

음수의 경우를 살펴보겠다.


a = -2147483648    // -2^31

a >>= 1;

a >>= 30;


연산 前, a = 1000 0000 0000 0000 0000 0000 0000 0000    // -2147483648 = -2^31

연산 中, a = 1100 0000 0000 0000 0000 0000 0000 0000    // -1073741824 = -2^30

연산 後, a = 1111 1111 1111 1111 1111 1111 1111 1111    // -1 = -2^0


양수와 음수의 >> 연산 결과가 다르다는 것을 눈치챘을 것이다.


>>연산자를 정리하자면,

비트 값들을 오른쪽으로 이동시킨 후에 왼쪽의 빈 공간에는

양수는 모두 0 으로,

음수는 모두 1 로 채운다.



(3) >>> 연산자

a = -2147483648    // -2^31

a >>>= 1;

a >>>= 30;


연산 前, a = 1000 0000 0000 0000 0000 0000 0000 0000    // -2147483648 = -2^31

연산 中, a = 0100 0000 0000 0000 0000 0000 0000 0000    // 1073741824 = 2^30

연산 後, a = 0000 0000 0000 0000 0000 0000 0000 0001    // 1 = 2^0


>>> 연산자는 부호를 신경쓰지 않는다.

그러므로 정리하자면,

비트 값들을 오른쪽으로 이동시킨 후에 왼쪽의 빈 공간은 모두 0 으로 채운다.



* 주의점 *

1. 연산 가능한 타입이 byte, short, int, long 타입과 char 타입이 있는데..

int 보다 작은 비트의 타입 byte, short, char 는 int 로 변환되서 Shift 가 된다고 한다.

그래서 결과값을 int 로 받지 않고 기존의 타입 그대로 받으면 문제가 발생하게 된다.


byte b = -1;

b = b >>> 2;    // error

b = (byte) b >>> 2;    // non-error but...


연산 前, b = 1111 1111 1111 1111 1111 1111 1111 1111;    // -1

연산 中, b = 0011 1111 1111 1111 1111 1111 1111 1111;    // 1073741823 = 2^30-1

연산 後, b = 1111 1111;                                              // -1, 위 결과의 오른쪽으로부터 8 bit 만 끊어서 캐스팅.


-1 값을 갖는 byte 변수에 >>> 연산을 했는데... 음수가 나오는 결과가 나오게 된다.


2. 비트 연산자의 피연산자 ( A << B 에서 B에 해당하는 부분 ) 그대로의 값으로

시프트 하는 것이 아니라 나머지 연산(%)의 결과값으로 시프트를 한다.


이해가 잘 안되니 예제를 보자.

int c = -1;

c = c >>> 32;


c 의 결과값은 32 % 32 = 0 이므로 아무런 시프트가 되지 않은 값, 똑같이 -1 을 갖는다.

byte 타입이라면 % 8, short 타입이라면 % 16, long 타입이라면 % 64 의 결과값으로 시프트 된다.



(출처 : 자바의神 Vol.1 , http://blog.naver.com/salagswk/150046360210,

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html)


by 뱁뱁 2014.05.26 20:57