Main Content

비트별 연산

여기에서는 MATLAB®에서 비트별 연산을 사용하여 숫자의 비트를 조작하는 방법을 보여줍니다. 비트에 대한 연산은 대부분의 현대적인 CPU에서 직접 지원됩니다. 많은 경우 이 방식으로 숫자의 비트를 조작하는 것이 나눗셈 또는 곱셈 같은 산술 연산을 수행하는 것보다 훨씬 빠릅니다.

숫자 표현

모든 숫자는 비트로 표현될 수 있습니다(이진라고도 함). 숫자의 이진 형식 또는 2진법에 포함된 1과 0은 해당 숫자에 2의 몇 거듭제곱이 있는지를 나타냅니다. 예를 들어, 7에 대한 8비트 이진 형식은 다음과 같습니다.

00000111

8비트 모음을 1바이트라고도 합니다. 이진 표현에서 비트는 오른쪽에서 왼쪽 방향으로 세기 때문에 이 표현의 첫 번째 비트는 1입니다. 이 숫자가 7을 나타내는 이유는 다음과 같습니다.

22+21+20=7.

MATLAB에 숫자를 입력하면 이 숫자는 배정밀도(64비트 이진 표현)인 것으로 간주됩니다. 하지만 단정밀도 숫자(32비트 이진 표현)와 정수(부호 있는/부호 없는 8비트 ~ 64비트)도 지정할 수 있습니다. 예를 들어, 숫자 7을 저장할 때 메모리 효율성이 가장 높은 방법은 8비트 부호 없는 정수를 사용하는 것입니다.

a = uint8(7)
a = uint8
    7

2진수 숫자 앞에 접두사 0b를 사용하여 직접 이진 형식을 지정할 수도 있습니다. (자세한 내용은 16진수 값과 2진수 값 항목을 참조하십시오.) MATLAB은 가장 적은 비트 수를 갖는 정수 형식으로 숫자를 저장합니다. 모든 비트를 지정하는 대신 가장 왼쪽 1과 그 오른쪽의 모든 자릿수만 지정하면 됩니다. 해당 비트 왼쪽의 비트는 중요하지 않은 0입니다. 따라서 숫자 7은 다음과 같이 지정됩니다.

b = 0b111
b = uint8
    7

MATLAB은 2의 보수를 사용하여 음의 정수를 저장합니다. 예를 들어, 8비트 부호 있는 정수 -8을 가정해 보겠습니다. 이 숫자에 대한 2의 보수 비트 패턴을 구하려면 다음 단계를 따르십시오.

  1. 숫자 8의 양수 값의 비트 패턴(00001000)을 먼저 적습니다.

  2. 그런 다음, 비트를 모두 뒤집습니다(11110111).

  3. 마지막으로 결과에 1을 더합니다(11111000).

결과 11111000이 -8의 비트 패턴입니다.

n = 0b11111000s8
n = int8
    -8

MATLAB은 기본적으로 숫자의 이진 형식을 표시하지 않습니다. 필요한 경우, 양의 정수에 대한 2진수 숫자의 문자형 벡터를 반환하는 dec2bin 함수를 사용할 수 있습니다. 여기서도, 이 함수는 자명한 0을 제외한 자릿수만 반환합니다.

dec2bin(b)
ans = 
'111'

bin2dec을 사용하여 두 형식 간에 전환할 수 있습니다. 예를 들어, 다음 명령을 사용하여 2진수 숫자 10110101을 10진수 형식으로 변환할 수 있습니다.

data = [1 0 1 1 0 1 0 1];
dec = bin2dec(num2str(data))
dec = 181

cast 함수와 typecast 함수도 서로 다른 데이터형 간 전환에 유용합니다. 이 함수들은 유사하지만 숫자의 기본 저장 공간을 처리하는 방식에 차이가 있습니다.

  • cast — 변수의 기본 데이터형을 변경합니다.

  • typecast — 기본 비트를 변경하지 않고 데이터형을 변환합니다.

MATLAB은 2진수 숫자를 직접 표시하지 않기 때문에 비트별 연산 작업을 할 때 데이터형에 주의해야 합니다. 일부 함수는 2진수 숫자를 문자형 벡터로 반환하고(dec2bin), 일부 함수는 십진수를 반환하고(bitand), 일부 함수는 비트 자체로 구성된 벡터를 반환합니다(bitget).

논리 연산자를 사용한 비트 마스크 적용

MATLAB에는 같은 길이의 이진수로 표현된 두 숫자를 대상으로 비트에 대한 논리 연산을 수행할 수 있는 여러 함수가 있습니다. 이러한 연산을 비트 마스크 적용이라고 합니다.

  • bitand 숫자가 모두 1이면 결과로 생성되는 숫자도 1입니다. 그렇지 않으면 숫자 0이 생성됩니다.

  • bitor둘 중 한 숫자가 1이면 결과로 생성되는 숫자도 1입니다. 그렇지 않으면 숫자 0이 생성됩니다.

  • bitxor — 숫자가 서로 다를 경우 결과로 생성되는 숫자는 1입니다. 그렇지 않으면 숫자 0이 생성됩니다.

위 함수 외에 bitcmp에서 비트별 보수를 사용할 수 있지만, 이는 한 번에 숫자 하나에서만 비트를 뒤집는 단항 연산입니다.

비트 마스크 적용은 특정 비트의 상태를 쿼리할 때 사용됩니다. 예를 들어, 이진수 00001000에 비트별 AND 연산을 사용하면 4번째 비트의 상태를 쿼리할 수 있습니다. 그런 다음, MATLAB이 0 또는 1을 반환할 수 있도록 해당 비트를 첫 번째 위치로 시프트할 수 있습니다. (다음 섹션에서 비트 시프트에 대해 자세하게 설명합니다.)

n = 0b10111001;
n4 = bitand(n,0b1000);
n4 = bitshift(n4,-3)
n4 = uint8
    1

비트별 연산을 교묘하게 활용할 수 있는 경우도 있습니다. 예를 들어, 숫자 n=8의 8비트 이진 표현을 가정해 보겠습니다.

00001000

8은 2의 거듭제곱이므로 이진 표현에는 1이 하나만 포함됩니다. 이제 숫자 (n-1)=7을 생각해 보겠습니다.

00000111

즉, 1을 빼면 가장 오른쪽 1부터 모든 비트가 뒤집힙니다. 결론적으로, n이 2의 거듭제곱이면 n(n-1)의 대응하는 숫자는 항상 다르며 비트별 AND는 0을 반환합니다.

n = 0b1000;
bitand(n,n-1)
ans = uint8
    0

하지만 n이 2의 거듭제곱이 아니면 가장 오른쪽 1은 20비트용이기 때문에, n(n-1)20비트를 제외하고 모두 동일한 비트를 가집니다. 이 경우, 비트별 AND는 0이 아닌 숫자를 반환합니다.

n = 0b101;
bitand(n,n-1)
ans = uint8
    4

이 연산을 이용하면 주어진 입력 숫자가 2의 거듭제곱인지 확인하기 위해 숫자의 비트를 대상으로 연산을 수행하는 간단한 함수를 작성할 수 있습니다.

function tf = isPowerOfTwo(n)
  tf = n && ~bitand(n,n-1);
end

쇼트서킷 AND 연산자 &&n이 0이 아님을 확인합니다. 0인 경우 함수는 정확한 답이 false인지 확인하기 위해 bitand(n,n-1)을 계산할 필요가 없습니다.

비트 시프트하기

비트별 논리 연산은 두 숫자에서 같은 위치의 비트를 비교하기 때문에, 비교할 비트를 변경할 수 있도록 비트를 이동할 수 있다면 편리할 것입니다. bitshift를 사용하여 이 작업을 수행할 수 있습니다.

  • bitshift(A,N)A의 비트를 왼쪽으로 N자릿수만큼 시프트합니다. 이것은 A2N곱하는 것과 동일합니다.

  • bitshift(A,-N)A의 비트를 오른쪽으로 N자릿수만큼 시프트합니다. 이것은 A2N으로 나누는 것과 동일합니다.

이러한 연산은 간혹 A<<N(왼쪽 시프트)과 A>>N(오른쪽 시프트)으로 표기되기도 하지만 MATLAB은 이러한 용도로 << 연산자와 >> 연산자를 사용하지 않습니다.

숫자의 비트를 시프트하면 일부 비트가 숫자 끝을 벗어나게 되며 새로 생성된 공간은 0 또는 1로 채워집니다. 비트를 왼쪽으로 시프트하면 오른쪽에 비트가 채워지고, 비트를 오른쪽으로 시프트하면 왼쪽에 비트가 채워집니다.

예를 들어, 숫자 8(이진 표현: 1000)의 비트를 오른쪽으로 한 자리 시프트하면 결과는 4(이진 표현: 100)가 됩니다.

n = 0b1000;
bitshift(n,-1)
ans = uint8
    4

마찬가지로 숫자 15(이진 표현: 1111)의 비트를 왼쪽으로 두 자리 시프트하면 결과는 60(이진 표현: 111100)이 됩니다.

n = 0b1111;
bitshift(15,2)
ans = 60

음수의 비트를 시프트하면 bitshift는 부호 있는 비트를 보존합니다. 예를 들어, 부호 있는 정수 -3(이진 표현: 11111101)을 오른쪽으로 두 자리 시프트하면 결과는 -1(이진 표현: 11111111)이 됩니다. 이러한 경우 bitshift0이 아니라 1로 왼쪽에 비트를 채웁니다.

n = 0b11111101s8;
bitshift(n,-2)
ans = int8
    -1

비트 쓰기

bitset 함수를 사용하여 숫자에서 비트를 변경할 수 있습니다. 예를 들어, 숫자 8의 첫 번째 비트를 1로 변경합니다(숫자에 1을 더함).

bitset(8,1)
ans = 9

기본적으로 bitset는 비트를 on 또는 1이 되도록 뒤집습니다. 선택적으로 세 번째 입력 인수를 사용하여 비트 값을 지정할 수 있습니다.

bitset는 한 번에 여러 개의 비트를 변경하지 않기 때문에 여러 비트를 변경하려면 for 루프를 사용해야 합니다. 따라서 변경하는 비트는 연속적일 수도 있고 비연속적일 수도 있습니다. 예를 들어, 이진수 1000의 처음 2개 비트를 변경해 보겠습니다.

bits = [1 2];
c = 0b1000;
for k = 1:numel(bits)
    c = bitset(c,bits(k));
end
dec2bin(c)
ans = 
'1011'

bitset를 사용하는 또 다른 일반적인 경우는 2진수 숫자로 구성된 벡터를 10진수 형식으로 변환할 때입니다. 예를 들어, 루프를 사용하여 정수 11001101의 개별 비트를 설정해 보겠습니다.

data = [1 1 0 0 1 1 0 1];
n = length(data);
dec = 0b0u8;
for k = 1:n
    dec = bitset(dec,n+1-k,data(k));
end
dec
dec = uint8
    205
dec2bin(dec)
ans = 
'11001101'

연속 비트 읽기

비트 시프트를 사용하는 또 다른 경우는 비트의 연속 섹션을 분리할 때입니다. 예를 들어, 16비트 숫자 0110000010100000에서 마지막 4개 비트를 읽어보겠습니다. 마지막 4개 비트는 이진 표현의 왼쪽에 있다는 점을 기억하십시오.

n = 0b0110000010100000;
dec2bin(bitshift(n,-12))
ans = 
'110'

숫자의 중간에서 연속 비트를 분리하려면 비트 시프트와 논리 마스크 적용을 함께 사용하면 됩니다. 예를 들어, 13번째 비트와 14번째 비트를 추출하려면 비트를 오른쪽으로 12자리 시프트한 다음, 결과로 나타나는 4개의 비트를 0011로 마스킹하면 됩니다. bitand에 대한 입력값이 동일한 정수 데이터형이어야 하므로, 0b11u16을 사용하여 0011을 부호 없는 16비트 정수로 지정할 수 있습니다. -u16 접미사가 없으면 MATLAB은 숫자를 부호 없는 8비트 정수로 저장합니다.

m = 0b11u16;
dec2bin(bitand(bitshift(n,-12),m))
ans = 
'10'

연속 비트를 읽을 수 있는 또 다른 방법은 숫자에서 지정된 비트를 읽어 들이는 bitget을 사용하는 것입니다. 읽을 연속 비트를 여러 개 지정하려면 콜론 표기법을 사용하십시오. 예를 들어, n의 마지막 8개 비트를 읽어보겠습니다.

bitget(n,16:-1:8)
ans = 1x9 uint16 row vector

   0   1   1   0   0   0   0   0   1

불연속 비트 읽기

bitget을 사용하면 숫자에서 읽을 비트가 차례대로 나열되어 있지 않은 경우에도 비트를 읽을 수 있습니다. 예를 들어, n에서 5번째 비트, 8번째 비트 및 14번째 비트를 읽어보겠습니다.

bits = [14 8 5];
bitget(n,bits)
ans = 1x3 uint16 row vector

   1   1   0

참고 항목

| | | | | |

관련 항목