비트 연산자

  • NOT
    • NOT 연산은 각 자릿수의 값을 반대로 바꾸는 연산이다.
    • NOT 0111 = 1000
    • x = ~y;
  • OR
    • OR 연산은 두 값의 각 자릿수를 비교해, 둘 중 하나라도 1이 있다면 1을, 아니면 0을 계산한다.
    • 0101 OR 0011 = 0111
    • x = y | z;
  • XOR
    • XOR 연산은 두 값의 각 자릿수를 비교해,값이 0으로 같으면 0, 값이 1로 같으면 0, 다르면 1을 계산한다.
    • 0101 XOR 0011 = 0110
    • x = y ^ z;
  • AND
    • AND 연산은 두 값의 각 자릿수를 비교해, 두 값 모두에 1이 있을 때에만 1을, 나머지 경우에는 0을 계산한다.
    • 0101 AND 0011 = 0001
    • x = y & z;

예제

  1. 0110 + 0110 = 1100
    • 0110 * 2와 같다 = (0110 « 1)
    • 0110을 왼쪽으로 한 번 시프트
    • 같은 값을 더하는 것은 왼쪽으로 한 번 시프트하는 것과 같다.
  2. 0100 * 0011 = 1100
    • 0100은 4와 같으므로, 0011에 4를 곱하면 답을 얻을 수 있다.
    • 2^n으로 곱하는것은 n만큼 왼쪽으로 시프트 하는 것과 같다.
    • 즉, 왼쪽으로 두 번 시프트 (0011«2)
  3. 1101 ^ (~1101) = 1111
    • 어떤비트를 NOT한 값과 그비트를 XOR하면 항상 1이다.
    • 즉, a^(~a)는 모든 비트가 1
  4. 1101 & (~0 « 2) = 10000
    • ~0은 모든 비트가 1이다.
    • « 2를 하면 마지막 비트 두 개는 0이되고, 나머지는 1이 된다.
    • « n을 하면 마지막 n개의 비트가 0이 된다.
    • 이 값과 다른 값을 AND 연산하면 마지막 두 비트의 값을 삭제한 값이된다.

비트 조작 할 때 알아야 할 사실들과 트릭들

  • Os는 모든 비트가 0인 값이고, 1s는 모든 비트가 1인 값을 나타낸다.
  • ^
    • x ^ 0s = x
    • x ^ 1s = ~x
    • x ^ x = 0
  • &
    • x & 0s = 0
    • x & 1s = x
    • x & x = x
  • |
    • x | 0s = x
    • x | 1s = 1s
    • x | x = x

2의 보수와 음수

컴퓨터는 일반적으로 정수를 저장할 때 2의 보수 형태로 저장한다. 양수를 표현할 때는 아무 문제 없지만 음수를 표현할 때는 그 수의 절댓값에 부호비트를 1로 세팅한 후 2의 보수를 취한 형태로 표현한다.

  • n비트 숫자에 대한 2의 보수는 2^n에 대한 보수값과 같다.
  • 이때, n은 부호비트를 뺀 나머지 값을 표현할 때 사용되는 비트의 수이다.
  • 2의 보수 표현 방법
    • -K를 N비트의 2진수로 표현하면 concat(1, 2^n-1 -K)이다.
    • 또는, 양수로 표현된 이진수를 뒤집고, 1을 더해준 후 부호비트를 맨 앞에 붙여주는 방법이 있다.
  • 예) -3
    • 3 //양수로 표현
    • 011 //이진수 변환
    • 100 // ~
    • 101 // + 1
    • 1101 // 부호 붙이기

산술 우측 시프트 vs 논리 우측 시프트

  • 산술 우측 시프트
    • 기본적으로 2로 나눈 결과와 같다.
    • >> 연산과 같다.
    • 부호비트는 바꾸지 않는다.
  • 논리 우측 시프트
    • 일반적으로 비트를 옮길 때 보이는 것처럼 움직인다.
    • >>> 연산과 같다.
    • 비트를 옆으로 옮긴 후 최상위 비트에 0을 넣는다.
    • 10110101 = -75
      • 01001010 (75) => 비트를 뒤집고 +1을 해주면 2의 보수로 표현된 음수를 얻을 수 있다.
    • 01011010 = 90

일작적인 비트 작업들

Get : 특정 비트 값 얻어내기

  • 1을 i비트만큼 시프트해서 00010000과 같은 값을 만든다.
  • AND 연산을 통해 num의 i번째 비트를 제외한 모든 비트를 삭제한다.
  • 이 값을 0과 비교하고, 값이 0이 아니라면 i번째 비트는 1이고, 0이라면 i번째 비트는 0이다.
      boolean getBit(int num, int i) {
          return ((num & (1 << i)) != 0);
      }
    

Set : 특정 비트값 채워넣기

  • 1을 i만큼 왼쪽으로 시프트하여 00010000과 같은 값을 만든다.
  • 그 다음 그 값을 num과 OR연산한다.
  • i번째를 제외한 나머지 비트들은 0과 OR연산을 하게 되므로 num에 아무 영향을 끼치지 않는다.
    int setBit(int num, int i) {
        return num | (1 << i);
    }
    

Clear: 특정 비트값 지우기

  • Set을 반대로 한 것과 같다.
  • NOT 연산자를 이용해 (00010000)을 11101111과 같이 만든 뒤 num과 AND 연산을 수행한다.
  • 그러면 나머지 비트의 값은 변하지 않은 채 i번째 비트값만 삭제된다.
    int clearBit(int num, int i) {
        int mask = ~(1 << i);
        return num & mask;
    }
    

Update : 특정 비트 값 갱신하기

  • setBit와 clearBit에서 이용한 방법을 섞어서 구현.
  • 우선 11101111과 같은 값을 이용해서 i번째 비트값을 삭제한다.
  • 그 뒤 바꾸자고 하는 값 v를 i번 쉬프트한다.
  • num의 i번째 비트만 v로 갱신한다.
    int updateBit(int num, int i, int v) {
      int mask = ~(1 << i);
      return (num & mask) | (v << i);
    }
    

참고

  • https://ko.wikipedia.org/wiki/비트_연산
  • 코딩인터뷰 완전정복