[Python] 비트 연산자 다양한 예제를 통해 이해하기

비트 연산자를 이해하기 위해서는 먼저 컴퓨터가 어떻게 숫자를 표현하는지 알아야 한다. 컴퓨터는 0과 1로 이루어진 이진수를 사용하여 정보를 저장하고 처리한다. 이러한 0과 1을 '비트(bit)'라고 부른다. 비트 연산자는 이 비트를 기반으로 연산을 수행하는 연산자들이다. 이제 각 연산자에 대해 자세히 살펴보겠다.


& (AND 연산자)

AND 연산자는 두 숫자의 이진 표현에서 같은 위치에 있는 비트가 모두 1일 때만 결과도 1이 되고, 그렇지 않으면 결과는 0이 된다.

예를 들어, 숫자 13과 10을 가지고 설명을 하면:

  • 13은 이진수로 1101으로 표현된다.
  • 10은 이진수로 1010으로 표현된다.

각 위치의 비트를 AND 연산에 따라 비교해보면:

  • 가장 오른쪽(1의 자릿수): 1과 0을 비교하면 0 (둘 다 1이 아니므로 0)
  • 그 다음 자릿수(2의 자릿수): 0과 1을 비교하면 0 (둘 다 1이 아니므로 0)
  • 그 다음 자릿수(4의 자릿수): 1과 0을 비교하면 0 (둘 다 1이 아니므로 0)
  • 가장 왼쪽(8의 자릿수): 1과 1을 비교하면 1 (둘 다 1이므로 1)

따라서, 1101 AND 1010의 결과는 1000이다. 10진수로는 8이다.

python
a = 13
b = 10
result = a & b
print(result)  # 8

위 코드는 13과 10의 AND 연산 결과를 출력한다. 출력 값은 8이다.


| (OR 연산자)

OR 연산자는 두 숫자의 이진 표현에서 같은 위치에 있는 비트 중, 하나라도 1이면 결과도 1이 되고, 둘 다 0이면 결과는 0이 된다.

이해를 돕기 위해, 숫자 6와 9을 예로 들어 설명을 해 보면:

  • 6은 이진수로 110으로 표현된다.
  • 9는 이진수로 1001로 표현된다.

각 위치의 비트를 OR 연산에 따라 비교해보면:

  • 가장 오른쪽(1의 자릿수): 0과 1을 비교하면 1 (하나라도 1이면 1).
  • 그 다음 자릿수(2의 자릿수): 1과 0을 비교하면 1 (하나라도 1이면 1)
  • 그 다음 자릿수(4의 자릿수): 1과 0을 비교하면 1 (하나라도 1이면 1)
  • 가장 왼쪽(8의 자릿수): 6은 이 자리에 대한 비트가 없으므로 0, 9는 1이므로, 0과 1을 비교하면 1이다.

따라서, 110 OR 1001의 결과는 1101이다. 10진수로는 13이다.

python
a = 6
b = 9
result = a | b
print(result)  # 13

위 코드는 6과 9의 OR 연산 결과를 출력한다. 출력 값은 13이다.


^ (XOR 연산자)

XOR 연산자는 두 숫자의 이진 표현에서 같은 위치의 비트가 서로 다르면 결과는 1, 같으면 0이 된다.

숫자 10과 12를 예로 들어본다.

  • 10은 이진수로 1010으로 표현된다.
  • 12는 이진수로 1100으로 표현된다.

각 위치의 비트를 XOR 연산에 따라 비교해보면:

  • 가장 오른쪽(1의 자릿수): 0과 0을 비교하면 0 (둘이 같으면 0)
  • 그 다음 자릿수(2의 자릿수): 1과 0을 비교하면 1 (둘이 다르면 1)
  • 그 다음 자릿수(4의 자릿수): 0과 1을 비교하면 1 (둘이 다르면 1)
  • 가장 왼쪽(8의 자릿수): 1과 1을 비교하면 0 (둘이 같으면 0)

따라서, 1010 XOR 1100의 결과는 0110이다. 10진수로는 6이다.

python
a = 10
b = 12
result = a ^ b
print(result)  # 6

위 코드는 10과 12의 XOR 연산 결과를 출력한다. 출력 값은 6이다.


~ (NOT 연산자)

NOT 연산자는 주어진 숫자의 이진 표현에서 모든 비트를 반전시킵니다. 즉, 1은 0으로, 0은 1로 바뀐다.

숫자 7을 예로 들어본다.

  • 7은 이진수로 111으로 표현된다.

각 위치의 비트를 NOT 연산에 따라 반전시키면:

  • 가장 오른쪽(1의 자릿수): 1은 0으로 반전된다.
  • 그 다음 자릿수(2의 자릿수): 1은 0으로 반전된다.
  • 가장 왼쪽(4의 자릿수): 1은 0으로 반전된다.

따라서, 111의 NOT 연산 결과는 000이다. 그러나 NOT 연산의 특징은 양의 정수는 음의 정수로, 음의 정수는 양의 정수로 바뀌게 되어 실제로는 -8이 된다.

python
a = 7
result = ~a
print(result)  # -8

위 코드는 7의 NOT 연산 결과를 출력한다. 출력 값은 -8이다.

참고: NOT 연산자에 대한 부분은 2의 보수 표현에 대한 추가적인 이해가 필요한다. 여기서는 간단하게 설명하였지만, 실제로 컴퓨터는 음수를 2의 보수 형태로 저장하고 이를 통해 뺄셈 연산을 수행한다. 이 때문에 NOT 연산의 결과가 위와 같이 나온다.


<< (왼쪽 시프트 연산자)

왼쪽 시프트 연산자는 숫자의 비트를 왼쪽으로 지정한 만큼 이동시킨다. 이 때, 왼쪽에서 넘어간 비트는 사라지고 오른쪽은 0으로 채워진다.

예를 들어 숫자 5를 2만큼 왼쪽으로 시프트한다고 해보자.

  • 5는 이진수로 101로 표현된다.

5를 2 비트 왼쪽으로 시프트하면:

  • 101 -> 10100

따라서, 101 << 2의 결과는 10100이다. 10진수로는 20이다.

python
a = 5
result = a << 2
print(result)  # 20

위의 코드는 5를 2만큼 왼쪽으로 시프트한 결과를 출력한다. 출력 값은 20이다.


>> (오른쪽 시프트 연산자)

오른쪽 시프트 연산자는 숫자의 비트를 오른쪽으로 지정한 만큼 이동시킨다. 이 때, 오른쪽에서 넘어간 비트는 사라지고 왼쪽은 원래 숫자의 최상위 비트(부호 비트)로 채워진다.

예를 들어 숫자 18을 2만큼 오른쪽으로 시프트한다고 해보자.

  • 18은 이진수로 10010로 표현된다.

18을 2 비트 오른쪽으로 시프트하면:

  • 10010 -> 00100

따라서, 10010 >> 2의 결과는 00100이다. 10진수로는 4이다.

python
a = 18
result = a >> 2
print(result)  # 4

위의 코드는 18을 2만큼 오른쪽으로 시프트한 결과를 출력한다. 출력 값은 4이다.

참고: 왼쪽 및 오른쪽 시프트 연산자는 주어진 숫자의 비트를 이동시키는 연산자이다. 주로 곱셈 및 나눗셈 연산을 빠르게 수행하기 위해 사용되며, 이러한 연산은 하드웨어 수준에서 매우 빠르게 처리된다.


비트 연산자의 활용

비트 연산자는 단순한 숫자 연산 외에도 다양한 분야에서 활용된다. 이러한 활용은 특히 저수준의 프로그래밍에서 더 자주 볼 수 있다. 다음은 비트 연산자의 주요 활용 방안들에 대한 설명이다.

1. 메모리 절약

비트 연산자는 변수에 여러 개의 플래그(flag)를 저장할 때 효율적이다. 각각의 플래그는 하나의 비트로 표현될 수 있으므로, 32비트 정수 변수 하나로 32개의 다른 상태나 옵션을 저장할 수 있다. 이는 여러 개의 boolean 변수를 사용하는 것보다 훨씬 메모리를 효율적으로 사용하게 해준다.

python
OPTION_A = 0b00000001  # 1
OPTION_B = 0b00000010  # 2
OPTION_C = 0b00000100  # 4

options = OPTION_A | OPTION_C  # OPTION_A와 OPTION_C 둘 다 활성화

2. 빠른 연산

비트 연산은 수학적 연산보다 대체로 빠르기 때문에 성능이 중요한 상황에서는 비트 연산을 사용하여 계산 속도를 향상시킬 수 있다. 예를 들어, 곱하기나 나누기 대신 비트 시프트를 사용하면 연산이 빨라질 수 있다.

python
number = 5
double_number = number << 1  # 5 * 2 = 10
half_number = number >> 1    # 5 / 2 = 2

3. 데이터 압축 및 암호화

비트 연산자는 데이터 압축 및 암호화에서 중요한 역할을 한다.

XOR 연산자는 같은 비트가 있으면 0을 반환하고, 다르면 1을 반환한다. 이 속성을 이용하여 간단한 암호화와 복호화에 사용될 수 있다. 예를 들어, 어떤 데이터와 키를 XOR 연산하면 원본 데이터는 변조되어 암호화된다. 그리고 암호화된 데이터에 동일한 키를 다시 XOR 연산하면 원본 데이터가 복구된다.

python
data = 0b10101010
key = 0b11110000

# 암호화
encrypted = data ^ key  # 결과: 0b01011010

# 복호화
decrypted = encrypted ^ key  # 원래의 data 값인 0b10101010이 복구된다.

4. 하드웨어 제어

비트 연산자는 하드웨어 제어, 특히 임베디드 시스템에서 매우 중요하다.

비트 설정 및 클리어 하드웨어의 특정 기능을 활성화하거나 비활성화하기 위해 사용되는 레지스터(register)에서 특정 비트를 설정하거나 클리어할 때 비트 연산자를 사용한다.

python
# 비트 설정 (OR 연산 사용)
register = 0b00000000
BIT3 = 0b00001000

register |= BIT3  # 3번째 비트 설정

# 비트 클리어 (AND 및 NOT 연산 사용)
register &= ~BIT3  # 3번째 비트 클리어

비트 토글 특정 비트의 상태를 바꾸려면 XOR 연산을 사용할 수 있다.

python
BIT2 = 0b00000100
register ^= BIT2  # 2번째 비트 토글

5. 마스킹 및 필터링

비트 마스크는 데이터의 특정 비트들만을 선택하거나, 특정 비트들을 변경하고자 할 때 사용한다.

예를 들어, 24비트 RGB 색상에서 빨간색, 녹색, 파란색 구성 요소를 분리하려면 다음과 같이 마스크를 사용할 수 있다.

python
color = 0x66CCFF  # 임의의 색상값

RED_MASK = 0xFF0000
GREEN_MASK = 0x00FF00
BLUE_MASK = 0x0000FF

red_component = (color & RED_MASK) >> 16
green_component = (color & GREEN_MASK) >> 8
blue_component = color & BLUE_MASK

이렇게 마스킹을 통해 RGB 색상의 각 구성 요소를 추출할 수 있다.

비트 연산자를 통한 마스킹은 효율적이며, 다양한 분야에서 널리 활용된다. 이러한 기법은 데이터 처리, 그래픽, 통신 분야 등에서 효과적으로 사용된다.


비트 연산자는 파이썬을 포함한 여러 프로그래밍 언어에서 강력한 도구로 자리잡고 있다. 초보자들에게는 처음에는 다소 낯설고 복잡해 보일 수 있지만, 제공된 간결한 예제와 설명을 통해 기본적인 개념을 습득하는 데 큰 도움이 되었기를 바란다.

이러한 연산자들은 다양한 분야와 상황에서 활용될 수 있는데, 특히 최적화와 효율성이 요구되는 경우에 그 진가를 발휘한다. 초기 단계에서는 이해하고 활용하는 데 어려움을 느낄 수 있지만, 프로그래밍 경험을 쌓아가면서 비트 연산자의 중요성과 활용 범위를 점점 더 깊게 이해할 수 있게 될 것이다. 그 때까지 꾸준한 학습과 연습을 통해 기본기를 탄탄하게 다져나가시기 바란다.


© Copyright 2023 CLONE CODING