C++ 비트 연산 최적화 기법

비트 연산은 정수 데이터를 이진수 단위로 다루는 저수준 연산으로, 알고리즘 문제 해결과 시스템 프로그래밍에서 핵심적인 역할을 한다. C++은 여섯 가지 비트 연산자를 제공하여 메모리 효율성과 실행 속도를 극대화할 수 있다.

핵심 연산자 동작 원리

AND 연산 (&)는 두 피연산자의 대응 비트가 모두 1일 때만 1을 반환한다. 플래그 검증이나 특정 비트 마스킹에 활용된다.

int x = 12;   // 1100
int y = 10;   // 1010
int r = x & y; // 1000 (8)

OR 연산 (|)는 어느 한쪽이라도 1이면 1을 반환하여 플래그 설정에 적합하다.

int r = x | y; // 1110 (14)

XOR 연산 (^)는 비트가 서로 다를 때 1을 반환하며, 토글 연산이나 암호화에 쓰인다.

int r = x ^ y; // 0110 (6)

NOT 연산 (~)는 모든 비트를 반전시켜 1의 보수를 생성한다.

unsigned int r = ~x; // ...11110011 (대형 양수)

시프트 연산은 비트 묶음을 좌우로 이동시킨다. 왼쪽 시프트(<<)는 2의 거듭제곱 곱셈, 오른쪽 시프트(>>)는 나눗셈에 상응한다.

int left = x << 2;   // 48, x * 4
int right = x >> 2;  // 3,  x / 4

실전 활용 패턴

최하위 비트 활용으로 홀짝 판별을 즉시 수행한다.

inline bool is_odd(int v) { return v & 1; }

임시 변수 없는 교환은 XOR의 결합법칙을 이용한다.

void exchange(int &p, int &q) {
    p ^= q;
    q ^= p;
    p ^= q;
}

2의 거듭제곱 검증은 비트 패턴 특성을 활용한다. 2의 거듭제곱 수는 단 하나의 1비트만 보유하므로, 자기 자신과 1 감소한 값의 AND가 0이 된다.

bool check_pow2(int v) {
    return v > 0 && !(v & (v - 1));
}

절대값 계산은 부호 비트 추출과 XOR 연산으로 구현한다.

int fast_abs(int v) {
    int sign = v >> 31;           // 부호 확장
    return (v ^ sign) - sign;
}

고급 최적화 기법

비트 제거 반복법으로 1의 개수를 센다. 매 반복마다 최하위 1비트를 제거한다.

int pop_count(int v) {
    int total = 0;
    while (v) {
        v &= v - 1;
        ++total;
    }
    return total;
}

마스크 기반 필드 조작은 특정 비트 영역을 정밀하게 제어한다.

// 하위 4비트 추출
int extract_lower = val & 0x0F;

// n번째 비트 설정 (0-indexed)
val |= (1U << n);

// n번째 비트 해제
val &= ~(1U << n);

// n번째 비트 토글
val ^= (1U << n);

2의 거듭제곱 정렬은 상한값을 구하는 데 사용된다.

int next_pow2(int v) {
    v--;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    return v + 1;
}

주의사항

시프트 연산에서 오른쪽 피연산자가 자료형 비트 수 이상이거나 음수인 경우 정의되지 않은 동작이 발생한다. 부호 있는 정수의 오른쪽 시프트는 구현 정의(implementation-defined)이며, 음수에 대한 논리 시프트는 C++에서 제공되지 않는다. 연산자 우선순위 혼란을 방지하기 위해 복합식에 괄호를 명시적으로 사용해야 한다.

태그: C++ Bit Manipulation low-level optimization competitive programming binary operations

7월 18일 19:43에 게시됨