알고리즘/기본코드

[연산자]비트연산자의 활용

  • -

이번 포스트에서는 비트 연산자에 대해 알아보자.

 

비트연산자(bitwise operator)

 

비트연산자란?

비트 연산자는 말 그대로 0, 1로 구성된 비트를 피 연산자로 하는 연산자로 &, |, ~, ^, <<, >>, >>> 7가지가 있다.

이중 <<, >>, >>>를 이동 연산자(shift operator)라고 하고 나머지 &, |, ~, ^를 논리연산자라고 한다.

&, |, !의 경우  피연산자가 boolean 타입을 가질 수 있는데 이들은 단지 true/false만을 판단하며 비트 연산자와 성격이 다르다.
true & false, !true 등

 

이동연산자(shift operator)

쉬프트 연산자는 <<와 >>가 있으며 각각 전체 비트를 왼쪽 또는 오른쪽으로 이동시킨다. 이때 밀리는 칸은 없어지고 추가되는 칸은 0으로 채워진다.

각 비트는 0/1의 두 가지로 구성되어있기 때문에 왼쪽으로 밀면 0/1의 두 가지 경우가 생겨서 *2의 효과가, 반대는 /2의 효과가 상긴다.

이 연산은 컴퓨터에 저장된 원형의 자료를 그대로 사용하기 때문에 *2, /2를 할 때 가장 빠른 속도를 낼 수 있다.

// 쉬프트 연산자
public static void bitShift() {
	int i = 100;
	System.out.printf("%4d - %10s%n", i, Integer.toBinaryString(i));
	// 왼쪽으로 밀 때 *2의 효과가 발생한다.
	System.out.printf("%4d - %10s : %d%n", i, Integer.toBinaryString(i << 1), i << 1);
	System.out.printf("%4d - %10s : %d%n", i, Integer.toBinaryString(i << 2), i << 2);
	// 오른쪽으로 밀면 반대로 /2의 효과가 발생한다.
	System.out.printf("%4d - %10s : %d%n", i, Integer.toBinaryString(i >> 1), i >> 1);
	System.out.printf("%4d - %10s : %d%n", i, Integer.toBinaryString(i >> 2), i >> 2);
}

이외에도  >>>  연산자가 있지만 거의 사용되지 않는다.

 

 

비트 논리연산자

논리 연산자는 &, |, ~, ^ 4가지가 사용된다.

연산자 기능
& 두 피 연산 비트 값이 모두 1인 경우만 1, 나머지는 0 ex) a & b
| 두 피 연산 비트 값이 모두 0인 경우만 0, 나머지는 1 ex) a | b
^ 두 피 연산 비트 값이 서로 다르면 1, 같으면 0 ex) a ^ b
~  연산자의 모든 비트를 반전시킴. a에 대한 1의 보수 ex) ~a
public static void bitLogical() {
	int i = 100;
	int j = 200;
	System.out.printf("%6d - %32s%n", i, Integer.toBinaryString(i));
	System.out.printf("%6d - %32s%n", j, Integer.toBinaryString(j));

	int andResult = i & j;	// 두 비트 모두 1이면 1
	int orResult = i | j;	// 두 비트 중 하나라도 1이면 1
	int xorResult = i ^ j;	// 두 비트가 서로 다르면 1
	int notResult = ~i;		// 기존 비트들을 모두 반전

	System.out.printf("%6s - %32s%n", "i & j", Integer.toBinaryString(andResult));
	System.out.printf("%6s - %32s%n", "i | j", Integer.toBinaryString(orResult));
	System.out.printf("%6s - %32s%n", "i ^ j", Integer.toBinaryString(xorResult));
	System.out.printf("%6s - %32s%n", "~i", Integer.toBinaryString(notResult));
}

 

 

bitmasking

 

배열을 이용한 논리값 관리

위의 두 가지 연산을 이용해서 비트마스킹 연산이라는 것을 할 수 있다. bitmasking이란 0/1로 구성된 bit를 마치 false/true로 간주해서 처리하는 기법이다.

 

예를 들어 집에 있는 32개의 전자제품에 대한 on/off 상태를 관리한다고 생각해보자. 무엇이 필요할까?

설마 32개의 변수를 선언한다고 하는 사람은 없을 것이고 가장 쉽게 떠오릴 수 있는 것은 boolean 타입의 배열이다. 아래는 1번 제품이 tv의 on/off 상태를 관리하는 모습을 배열로 작성한 예이다.

static void byArray() {
    boolean[] status = new boolean[9];
    int product = tv; // 1
    // TV 상태 확인
    System.out.printf("제품이 켜져있나? %b%n", status[product]);
    // TV를 켜보자.
    status[tv] = true;
    System.out.printf("제품이 켜졌나? %b%n", status[product]);
    // TV를 꺼보자.
    status[tv] = false;
    System.out.printf("제품이 꺼졌나? %b%n", status[product]);
    // TV의 상태를 반전시켜보자.
    status[tv] = !status[tv];
    System.out.printf("제품이 토글되었나? %b%n", status[product]);
        
    // 이제 radio를 관리해볼까?
    product = radio;
}

status라는 배열에 제품의 상태를 저장하고 index를 이용해서 제품을 찾아가며 boolean 값 기반으로 관리하는 모습을 볼 수 있다.

 

이제 이 과정을 bitmasking으로 처리해보자. on/off를 저장할 수 있는 bit 32개를 가진 int는 이미 훌륭한 논리값 저장의 도구다(long은 무려 64개!!)

 

그런데 문제는 사용하는 방법이 배열과는 많이 다르다는 점이다.

 

 

bitmasking

다음은 boolean 배열을 만들었을 때와 int를 이용한 bitmasking을 활용한 예이다.

  boolean 배열 int bitmasking
타입 객체(상태 전달 시 주소의 복사본  전달 - 공유됨) 단순 값(상태 전달 시 값의 복사본 전달 - 독립됨)
요소 위치 정수형 index 2진수에서 1의 위치(0b001, 0b010)
위치 지정 i - 인덱스를 사용하면 끝 1<<i 처럼 shift 연산자로 1을 이동시켜서 위치 지정
상태 확인 status[i] (status & (1<<i)) >0 => status에서 i 번째 index가 1이다!
켜기 status[i]=true status |=(1<<i)
끄기 status[i]=false status &=~(1<<i)
반전시키기 status[i] = !status[i]; status ^=(1<<i)
public static void bitMasking() {
	// 32개의 0/1관리
	int status = Integer.MAX_VALUE;
	System.out.printf("%10d - %32s%n", status, Integer.toBinaryString(status));
	// 10번째 비트가 켜져있나?
	System.out.printf("현상태: - 켜져있나? %b%n", (status & (1 << 10)) > 0);
	// 10번째 비트를 꺼보자. : 비교 비트만 끈 상태에서 & 연산
	status &= ~(1 << 10);
	System.out.printf("꺼보기: - 켜져있나? %b%n", (status & (1 << 10)) > 0);
	// 10번째 비트를 켜보자 : 비교 비트만 켠 상태에서 | 연산
	status |= 1 << 10;
	System.out.printf("켜보기: - 켜져있나? %b%n",  (status & (1 << 10)) > 0);
	// 10번째 비트를 토글시켜보자.
	status ^= 1 << 10;
	System.out.printf("토글 1: - 켜져있나? %b%n",  (status & (1 << 10)) > 0);
	status ^= 1 << 10;
	System.out.printf("토글 2: - 켜져있나? %b%n",  (status & (1 << 10)) > 0);
}

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.