WeniVooks

검색

견고한 파이썬

비트연산

비트연산은 컴퓨터 과학에서 기본 연산으로 주로 알고리즘 문제 해결에서 자주 등장하지만, 실무에서는 권한 관리나 시스템 최적화, 네트워킹 등의 특수한 상황에서만 중요한 역할을 합니다.

비트연산자는 두 이진수(0과 1로 구성된 숫자)에 대해 비트 단위로 논리 연산을 수행합니다. 이러한 연산자에는 주로 AND(&), OR(|), XOR(^), NOT(~), 비트 시프트(<<, >>) 등이 있습니다.

비트연산자를 사용할 때의 일반적인 흐름은 다음과 같습니다.

  1. 10진수를 2진수로 변환: 컴퓨터는 내부적으로 모든 데이터를 이진수로 처리하기 때문에, 비트 연산을 수행하기 전에 먼저 관련 데이터를 이진수 형태로 변환합니다.
  2. 2진수에서 비트연산을 전개: 변환된 이진수에 대해 원하는 비트연산을 수행합니다. 예를 들어, 두 숫자에 대해 비트 AND 연산을 수행하면, 두 이진수 모두에서 1인 비트 위치에서만 결과가 1이 되는 새로운 이진수를 얻게 됩니다.
  3. 2진수를 10진수로 변환: 연산 결과로 나온 이진수를 다시 10진수로 변환하여 사용합니다. 이 과정은 연산 결과를 사람이 이해하기 쉬운 형태로 만들거나, 다른 10진수 기반의 연산이나 처리와 호환될 수 있도록 합니다.

비트연산의 개념과 흐름을 이해하는 것은 컴퓨터 프로그래밍의 깊이를 더하는 데 중요합니다. 이를 통해 더 효율적이고, 최적화된 코드를 작성할 수 있는 능력을 키울 수 있습니다.

1. 비트 and(&) 연산자

비트 and 연산자 &는 두 비트열의 동일한 위치에 있는 비트가 모두 1일 경우에만 결과로 1을 반환하는 연산자입니다. 이 연산자는 주로 비트 레벨에서 데이터의 특정 부분을 마스킹하는데 사용됩니다.

예를 들어, 아래의 예제에서는 두 수 60과 13에 대해 비트 AND 연산을 수행합니다.

a = 60 # 60 = 0011 1100 b = 13 # 13 = 0000 1101 result = a & b # 12 = 0000 1100 print(result) # 출력: 12

이 연산의 계산 과정은 다음과 같습니다:

  1. 이진수 변환: 먼저, a의 값인 60과 b의 값인 13을 이진수로 변환합니다.
    • 60의 이진수: 0011 1100
    • 13의 이진수: 0000 1101
  2. 비트 AND 연산: 각 위치의 비트를 비교하여 두 비트 모두 1일 경우에만 결과의 해당 위치에 1을 배치합니다.
    • 0011 1100 (60의 이진수)
    • 0000 1101 (13의 이진수)
    • 0000 1100 (AND 연산 결과)
  3. 결과의 이진수를 10진수로 변환: 연산 결과인 0000 1100을 10진수로 변환합니다. 이 값은 12입니다.

이처럼 비트 AND 연산자는 각 비트 위치에 대해 논리적 'AND' 연산을 수행하여, 두 입력값이 모두 1인 경우에만 결과로 1을 반환합니다.

위 내용을 코드 블록으로 요약한 것입니다.

# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# b의 값 13을 2진수로 풀어줍니다.
0000 1101
 
# 위아래로 AND 연산을 수행합니다.
0011 1100
0000 1101
 
# & 연산의 경우 위와 아래 한짝씩 맞춰 and 연산을 수행합니다.
# 이때 1 = True, 0 = False 입니다.
0011 1100
0000 1101
---------
0000 1100
 
# 결과값을 다시 10진수로 변환합니다.
12
# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# b의 값 13을 2진수로 풀어줍니다.
0000 1101
 
# 위아래로 AND 연산을 수행합니다.
0011 1100
0000 1101
 
# & 연산의 경우 위와 아래 한짝씩 맞춰 and 연산을 수행합니다.
# 이때 1 = True, 0 = False 입니다.
0011 1100
0000 1101
---------
0000 1100
 
# 결과값을 다시 10진수로 변환합니다.
12

2. 비트 or(|) 연산자

비트 or 연산자 |는 두 비트열의 동일한 위치에 최소 하나의 비트가 1일 경우 결과로 1을 반환합니다. 이 연산자는 특정 비트를 설정하거나 두 값 중 하나라도 1인 비트를 확인할 때 유용합니다.

아래 예시에서는 두 수 60과 13에 대해 비트 OR 연산을 수행합니다.

a = 60 # 60 = 0011 1100 b = 13 # 13 = 0000 1101 result = a | b # 61 = 0011 1101 print(result) # 출력: 61

이 연산의 과정은 다음과 같습니다:

  1. 이진수 변환: 먼저, a와 b의 값인 60과 13을 이진수로 변환합니다.
    • 60의 이진수: 0011 1100
    • 13의 이진수: 0000 1101
  2. 비트 OR 연산: 각 위치의 비트를 비교하여 두 비트 중 하나라도 1이면 결과의 해당 위치에 1을 배치합니다.
    • 0011 1100 (60의 이진수)
    • 0000 1101 (13의 이진수)
    • 0011 1101 (OR 연산 결과)
  3. 결과의 이진수를 10진수로 변환: 연산 결과인 0011 1101을 10진수로 변환합니다. 이 값은 61입니다.

비트 OR 연산자는 데이터의 특정 비트를 '켜기' 위한 설정이나, 여러 조건 중 하나라도 충족하는지 확인할 때 주로 사용됩니다. 예를 들어, 사용자 권한 설정에서 여러 권한 중 하나라도 부여되어야 하는 경우 비트 OR 연산을 통해 이를 쉽게 구현할 수 있습니다.

위 내용을 코드 블록으로 요약한 것입니다.

# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# b의 값 13을 2진수로 풀어줍니다.
0000 1101
 
# 위아래로 붙여보겠습니다.
0011 1100
0000 1101
 
# 연산의 경우 위와 아래 한짝씩 맞춰 or 비교를 해봅니다.
# 이때 1 = True, 0 = False 입니다.
0011 1100
0000 1101
---------
0011 1101
 
# 결과값을 다시 10진수로 변환해줍니다.
61
# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# b의 값 13을 2진수로 풀어줍니다.
0000 1101
 
# 위아래로 붙여보겠습니다.
0011 1100
0000 1101
 
# 연산의 경우 위와 아래 한짝씩 맞춰 or 비교를 해봅니다.
# 이때 1 = True, 0 = False 입니다.
0011 1100
0000 1101
---------
0011 1101
 
# 결과값을 다시 10진수로 변환해줍니다.
61

3. 비트 xor(^) 연산자

비트 xor 연산자 ^는 두 비트열의 동일한 위치에 있는 비트가 서로 다를 경우에만 1을 반환합니다. 이 연산자는 비교되는 두 값이 다를 때 사용되며, 암호화, 오류 검출, 값의 토글 등 다양한 상황에서 사용됩니다.

다음은 두 수 60과 13에 대해 비트 XOR 연산을 수행하는 예시입니다.

a = 60 # 60 = 0011 1100 b = 13 # 13 = 0000 1101 result = a ^ b # 49 = 0011 0001 print(result) # 출력: 49

이 연산의 과정은 다음과 같습니다:

  1. 이진수 변환: 먼저, a와 b의 값인 60과 13을 이진수로 변환합니다.
    • 60의 이진수: 0011 1100
    • 13의 이진수: 0000 1101
  2. 비트 XOR 연산: 각 위치의 비트를 비교하여 서로 다를 경우(하나는 1, 다른 하나는 0) 결과의 해당 위치에 1을 배치합니다.
    • 0011 1100 (60의 이진수)
    • 0000 1101 (13의 이진수)
    • 0011 0001 (XOR 연산 결과)
  3. 결과의 이진수를 10진수로 변환: 연산 결과인 0011 0001을 10진수로 변환합니다. 이 값은 49입니다.

비트 XOR 연산자는 두 값이 서로 다른 경우를 찾을 때 사용됩니다. 예를 들어, 두 데이터 집합에서 변경된 부분을 찾거나, 데이터를 암호화 및 복호화하는 데 사용할 수 있습니다. 또한, 같은 값을 두 번 XOR 연산하면 원래 값으로 돌아오는 성질을 가지고 있어, 데이터의 일시적인 저장과 복원에도 사용됩니다.

위 내용을 코드 블록으로 요약한 것입니다.

# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# b의 값 13을 2진수로 풀어줍니다.
0000 1101
 
# 위아래로 붙여보겠습니다.
0011 1100
0000 1101
 
# 연산의 경우 위와 아래 한짝씩 맞춰 xor 비교를 해봅니다.
# 이때 1 = True, 0 = False 입니다.
0011 1100
0000 1101
---------
0011 0001
 
# 결과값을 다시 10진수로 변환해줍니다.
69
# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# b의 값 13을 2진수로 풀어줍니다.
0000 1101
 
# 위아래로 붙여보겠습니다.
0011 1100
0000 1101
 
# 연산의 경우 위와 아래 한짝씩 맞춰 xor 비교를 해봅니다.
# 이때 1 = True, 0 = False 입니다.
0011 1100
0000 1101
---------
0011 0001
 
# 결과값을 다시 10진수로 변환해줍니다.
69

4. 비트 not(~) 연산자

비트 not 연산자 ~는 주어진 비트열의 모든 비트를 반전시키는 연산자입니다. 이 연산자는 1을 0으로, 0을 1로 바꿉니다. 비트 NOT 연산은 종종 데이터의 비트 패턴을 전환하거나, 특정 비트를 '끄는' 데 사용됩니다.

아래 예시에서는 수 60에 대해 비트 NOT 연산을 수행합니다.

a = 60 # 60 = 0011 1100 result = ~a # -61 = 1100 0011 print(result) # 출력: -61

이 연산의 과정은 다음과 같습니다:

  1. 이진수 변환: 먼저, a의 값인 60을 이진수로 변환합니다.
    • 60의 이진수: 0011 1100
  2. 비트 NOT 연산: 모든 비트를 반전시킵니다. 1은 0으로, 0은 1로 바뀝니다.
    • 0011 1100 (60의 이진수)
    • -------
    • 1100 0011 (NOT 연산 결과)
  3. 결과의 이진수를 10진수로 변환: 연산 결과인 1100 0011을 10진수로 변환합니다. 이 경우, 결과는 -61입니다.

여기서 중요한 점은, 대부분의 컴퓨터 시스템에서 정수는 2의 보수 형태로 표현된다는 것입니다. 따라서, 비트 NOT 연산 후의 결과는 원래 수의 음의 값에서 1을 뺀 값이 됩니다. 예를 들어, ~60의 결과는 -61이 됩니다. 이는 60을 2진수로 표현하고 모든 비트를 반전시킨 후, 이 결과를 2의 보수 형태로 음의 정수로 해석하기 때문입니다.

위 내용을 코드 블록으로 요약한 것입니다.

# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# 연산의 경우 부정을 취합니다.
0011 1100
----
1100 0011
 
# 결과값을 다시 10진수로 변환해줍니다.
-61
# a의 값 60을 2진수로 풀어줍니다.
0011 1100
 
# 연산의 경우 부정을 취합니다.
0011 1100
----
1100 0011
 
# 결과값을 다시 10진수로 변환해줍니다.
-61

5. 비트 shift 연산자

비트 Shift 연산자는 비트열을 왼쪽 또는 오른쪽으로 이동시키는 연산자입니다. 이 연산자는 데이터의 이진 표현을 효율적으로 조작하고, 곱셈 및 나눗셈 연산을 빠르게 수행하는 데 유용합니다.

  • 왼쪽 시프트 연산자(<<): 비트열을 왼쪽으로 이동시키며, 오른쪽 끝에 빈 자리는 0으로 채워집니다. 왼쪽 시프트 연산은 주어진 비트 수만큼 2의 거듭제곱을 곱하는 것과 동일합니다.
  • 오른쪽 시프트 연산자(>>): 비트열을 오른쪽으로 이동시키며, 왼쪽 끝에 빈 자리는 0(부호가 없는 정수) 또는 부호 비트(부호가 있는 정수)로 채워집니다. 오른쪽 시프트 연산은 주어진 비트 수만큼 2의 거듭제곱으로 나누는 것과 동일합니다.
a = 60 # 60 = 0011 1100 result = a << 2 # 240 = 1111 0000 print(result) # 출력: 240 # 비트열을 왼쪽으로 이동시키며, 빈 자리는 0으로 채웁니다. result = a >> 2 # 15 = 0000 1111 print(result) # 출력: 15 # 비트열을 오른쪽으로 이동시키며, 빈 자리는 0으로 채웁니다.

이제, 숫자 7에 대해 왼쪽 Shift 연산을 수행하는 예를 살펴보겠습니다.

입력

a = 7 # 7 = 0111 b = 2 print(a << b) # 28 = 1 1100

계산 과정은 다음과 같습니다:

  1. 이진수 변환: a의 값인 7을 이진수로 변환합니다.
    • 7의 이진수: 0111
  2. Shift 연산: a의 비트열을 왼쪽으로 b(2)만큼 이동시킵니다. 오른쪽 끝의 빈 자리는 0으로 채워집니다.
    • 0111 (7의 이진수)
    • 1 1100 (왼쪽 Shift 연산 결과)
  3. 결과의 이진수를 10진수로 변환: 연산 결과인 1 1100을 10진수로 변환합니다. 이 값은 28입니다.

위 내용을 코드 블록으로 요약한 것입니다.

# a의 값 3을 2진수로 바꿉니다.
0111
 
# b 만큼 이동할 것이기 때문에 b는 바꾸지 않음
 
# a의 값들(집합)을 왼쪽으로 b만큼 이동시키고 뒤에 0을 붙임
011100
 
# 결과 값을 10진수로 다시 변환합니다.
28
# a의 값 3을 2진수로 바꿉니다.
0111
 
# b 만큼 이동할 것이기 때문에 b는 바꾸지 않음
 
# a의 값들(집합)을 왼쪽으로 b만큼 이동시키고 뒤에 0을 붙임
011100
 
# 결과 값을 10진수로 다시 변환합니다.
28

6. 2의 보수

2의 보수는 컴퓨터 시스템에서 음수를 표현하는 데 주로 사용되는 방법입니다. 이 방식을 사용하면 덧셈, 뺄셈과 같은 연산이 간단해지며, 음수와 양수 사이의 전환도 쉽게 할 수 있습니다.

예를 들어, 숫자 40의 2의 보수를 구하는 과정은 다음과 같습니다.

a = 40
~a #bit NOT(2의 보수)
 
101000
 
#비트단위를 뒤짚었습니다.
010111
 
#비트단위를 뒤짚고 +1했습니다.
011000
 
101000
011000
000000 #자기 자신와 보수의 값을 더하면 값은 0이 나옵니다.
 
#1. 0을 2개로 표현하지 않아도 됩니다.
#2. 값을 더했을 경우 올림수 하나만 버리면 됩니다.
a = 40
~a #bit NOT(2의 보수)
 
101000
 
#비트단위를 뒤짚었습니다.
010111
 
#비트단위를 뒤짚고 +1했습니다.
011000
 
101000
011000
000000 #자기 자신와 보수의 값을 더하면 값은 0이 나옵니다.
 
#1. 0을 2개로 표현하지 않아도 됩니다.
#2. 값을 더했을 경우 올림수 하나만 버리면 됩니다.

1의 보수는 비트를 완전히 뒤집는 방법입니다. 하지만, 이 방법을 사용하면 -0과 +0의 문제가 발생합니다. 대부분의 현대 컴퓨터 시스템은 2의 보수 방식을 사용합니다. 이 방식에서는 0을 00000000로 표현하고, 1부터는 비트 단위 보수를 취한 후 +1을 더합니다. 2의 보수를 사용하면 오버플로우가 발생하여 절대값이 같은 음수와 양수를 더했을 때 결과가 0이 되는 특성이 있습니다.

{"packages":["numpy","pandas","matplotlib","lxml"]}
14.3 파일 입출력 연습문제14.5 f-string 문법과 서식 지정자