ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java-14] 비트 연산자와 쉬프트 연산자
    Java 2020. 11. 27. 14:12

    Bitwise and Shift Operator

     

    1. 비트 연산자 (Bitwise)

     

    비트연산과 쉬프트 연산은 low-level 의 연산자이다. 보통 개별의 비트를 integer 값으로 만들려 할 때, 많이 사용하게 된다. 비트 연산은 사실 low-lovel(네트워킹 작업)을 하는 경우를 제외하고는 모던 자바쪽에서는 잘 사용하지 않는 연산이긴 하다. 보통 flag 를 이용한 테스트를 위해 사용한다고 한다. flag에 대해서 알고 싶다면 이 블로그를 참고하면 좋다. 일단 우리가 bit를 사용하는 연산을 이해하기 위해서는 ‘바이너리(binary)’와 ‘보수(음의 정수 표현을 위해)’에 대해서도 알고 있어야 한다.

     

    바이너리와 보수에 대해서는 따로 언급하지는 않고, 적절한 공부 링크를 적어서 학습할 수 있도록 하자 : )

     

    우리가 비트연산자를 사용할 때, 실수(Double), Boolean, 배열(Array), 객체(Object)에는 사용할 수 없다. 아까 말했듯이 비트 연산은 low-level의 연산을 위해 있다고 말을 했다. 만약 boolean 연산자를 사용하고 싶다면 | , &, ^ 연산을 사용해야 하는데, 이 부분에 대해서는 논리연산자를 나중에 알아보도록 하자. 

     

    쉬프트 연산과 비트 연산을 할 경우, 한쪽이 Long 타입이면 연산되는 부분은 Long타입(8바이트) 으로 그렇지 않다면 Int(4바이트) 로 연산 된다. 이제 몇가지 연산을 알아보도록 하자. 

     

    비트 보수 연산자 ( ~ ) (Bitwise complement)

    • 단일 ~ 연산은 우리가 보수를 알기 위해서 연산으로 알고 있거나, Not 으로 알려져 있다. ~ 단일 연산은 기본적으로 수의 반대로 잡아준다. 예를들어서

    다음과 같은 연산을 한다고 했을 때, 12의 바이너리 표현은 [1 1 0 0] 인데 이를 보수 연산을 하면 [0 0 1 1] 으로 바뀌게 된다. 그래서 출력된 바이너리를 보고싶어서 Integer.toBinaryString() 메서드를 상용했는데, 보수 연산을 하여 32비트(8바이트 로 변경된다.

    0XFF 를 사용하면 8비트로 좀 더 깔끔하게 표현이 가능하다. 

     

    비트 And 연산자 ( & ) (Bitwise AND)

    • ‘And’( & ) 연산은 두개의 정수형 연산자를 각 비트의 Boolean 의 And 연산을 진행하게 된다. 아직 Boolean 에 대해서 다루지 않아서, 어려울 수 있는데, 각 비트라고 하면 0 0 0 0 의 각  0 의 비트와 연산한다고 생각하면 된다. 그리고 and 연산이라 한다면 만약 같은 자리에 1이 동시에 있다면 1을 반환한다고만 알아두도록 하자. ex) 010 & 110 이면 2번째의 1만 서로 같으므로 2번째 비트만 1을 반환한다. 그러므로 010 이 된다.

    이런식으로 값이 나오게 된다. 한번 직접 해보면 좋겠다. 

     

    비트 OR 연산자 ( | ) (Bitwise OR)

    • 이 OR 연산자는 두개의 정수 피연산자를 논리(Boolean)의 OR 연산을 진행한다. 이 연산자는 무조건 둘중에 하나라도 비트에 1이 들어가있으면, 무조건 1을 반환한다. 0|1 = 1 , 1|1 = 1 이런식으로 말이다.

     

     

    비트 XOR 연산자 ( ^ ) (Bitwise XOR)

    • XOR 연산자는 두개의 정수의 비트를 XOR 연산한다고 생각하면 된다. Exclusive OR 이라고도 하는데, 결론적으로는 두개의 비트가 1로 같으면 0 을 반환하게 된다. 그리고 같지 않으면 1을 반환한다. 예를 들어서 

    1^1 = 0, 0^0 = 0, 1^0 = 1, 0^1 = 1 을 반환한다. 그래서 Exclusive OR이라고도 한다.

     

    Left 쉬프트 연산 (<<) Left shift

    • 정말 간단하다 비트를  옮긴다고 생각을 하면 된다. 옮긴다보다는 “비트를 왼쪽으로 밀어버린다”라고 생각하는게 옳을 수 있다. 즉 0001 이라는 비트가 있다고 가정해보자 이를 <<1 을 한다면 1칸 밀어버려서 0010 이 된다. 그리고 <<2 를 한다고 하면 0100 이 된다.

    0001 = 1 

    0010 << 1 = 2

    0100 << 2 = 4

    뭔가 규칙이 있어보이지 않나? 이 쉬프트 연산은 2^n의 특징을 가지고 있다. 1을 만큼 쉬프트 연산을 진행한다고 하면, 2^1 의 숫자가 int형에 곱해진다고 생각하면 된다. 참 신기하다.  

     

     

    Right 쉬프트연산자 ( >> ) (Right shift)

     

    • left 반대로 오른쪽으로 민다고 생각하면 된다. Left 연산은 2^n 을 곱했지만 Right에서는 2^n을 나눈다. 간단하게 예시를 보자

    • 만약 연산하려는 피 연산자가 음수이면 어떻게 될까? 신기하게 밀어버려서 빈 공간을 1로 채워준다. 이는 음수를 지속적으로 유지시키기 위한 것이다.

     

    Unsigned Right 쉬프트연산자 ( >>> ) 

    • >>> 연산은 결국에는 쉬프트해서 밀어버린 부분을 전부 0으로 채워주는 것이다. 이게 사실 어떠한 정수에 부호를 신경쓰지 않기에 가능한 건데, 말로 설명하는 것보다 직접 보는게 빠르다. 만약 32비트의 다음과 같은 음수가 있다고 가정해보자

    b = 1000 0000 0000 0000 0000 0000 0000 0000 을 가지고 있다. 이걸 >>>1 만큼 오른쪽으로 민다고 가정하면

    b >>> 1 = 0100 0000 0000 0000 0000 0000 0000 0000 이고 음수를 보장하지 않는다. 예를 보자

     

    'Java' 카테고리의 다른 글

    [Java-16] 논리 연산자  (0) 2020.11.27
    [Java-15] 관계 연산자  (0) 2020.11.27
    [Java-13] 자바 산술 연산자  (0) 2020.11.27
    [Java-12] 자바 배열  (0) 2020.11.20
    [Java-11]타입추론 var  (1) 2020.11.20

    댓글

Designed by Tistory.