본문 바로가기
밑바닥부터 시작하는 게임 개발/C++

[밑바닥 C++] 4. 컴퓨터 친화적인 비트 연산자 + 축약, 증감 연산자

by KK1 2021. 7. 17.

비트 단위 연산자

정수 자료형만 비트 단위로 연산을 할 수 있습니다.

컴퓨터 친화적이진수를 활용한 연산자가장 빠른 연산자입니다. 

이번 내용을 이해하기 위해서는 2진수, 8진수, 10진수, 16진수 등 진수에 대한 내용을 학습하는 것을 추천합니다.

특수한 기호로 표시한 비트 연산자를 배운 기억이 있을 수 있습니다. 

& (AND, 논리곱)

AND 진리표

논리 연산자 &&(and)를 하나만 사용하여 &(논리곱) 연산자로 사용합니다.

위 표와 같이 두 비트가 1일 때 결과는 1, 하나라도 0이라면 결과는 0이 나옵니다.

short Num = 10 & 8;
 
0000 0000 0000 1010
        &
0000 0000 0000 1000
-------------------
0000 0000 0000 1000

위 표현에서 short를 사용한 이유는 값의 변화를 16bit의 이진수로 보여주기 위함이다.

| (OR, 논리합)

OR 진리표

논리 연산자 ||(or)를 하나만 사용하여 |(논리합) 연산자로 사용합니다.

위 표와 같이 두 비트 중 하나라도 1일 때 겨로가는 1, 둘다 0일 때만 결과는 0입니다.

short Num = 10 & 8;
 
0000 0000 0000 1010
        |
0000 0000 0000 1000
-------------------
0000 0000 0000 1010

^ (XOR, exclusive OR, 배타적 논리합)

XOR 진리표

^(배타적 논리합) 연산자는 두 비트 중 하나라도 1이라면 결과는 1, 둘 다 0일 때에만 결과가 0입니다.

short Num = 10 ^ 8;
 
0000 0000 0000 1010
        ^
0000 0000 0000 1000
-------------------
0000 0000 0000 0010

~ (NOT, 부정)

NOT 진리표

모든 비트열을 반전시킵니다. -> 1의 보수를 진행한 상태가 됩니다. 

short Num = ~1;
       
        ~
0000 0000 0000 0001
-------------------
1111 1111 1111 1110

이 상태에서 1을 더해주면 2의 보수가 됩니다. 1의 보수와 2의 보수에 대해서는 어떤 내용인지 구글링을 추천드립니다.

short Num = (~1) + 1 = -1;
-------------------
1111 1111 1111 1110
0000 0000 0000 0001
-------------------
1111 1111 1111 1111

2의 보수가 되었을 때 값은 -1이 나옵니다. 아래에 추가 내용으로 자세히 보겠습니다.

 

아래의 short 자료형 숫자 1과 -1을 표현하는 비트를 봅니다.

숫자 1과 -1의 비트 표현

비트의 끝(왼쪽에서 시작해서 오른쪽 끝)은 부호 비트라고 하여 signed 데이터의 +, - 표현에 사용합니다.

비트 한개로 +, -를 표현할 수 있으나 그 범위는 절반이 됩니다.

부호비트가 활성화 된 상태에서 모든 비트는 반대로 값이 감소합니다.

모두 켠 상태에선 -1이며  가장 오른쪽 비트를 끄면 추가로 1이 감소되어 -2가 표현됩니다.

 

예를 들어 숫자 8을 -8로 표현한다면 0000 1000 → 1111 1000(8개의 비트만 표현한다면)이 됩니다. (1, 2, 4 만큼 감소)

이때 위에서 본 2의 보수를 통하여 양수와 음수의 변환 관계를 알 수 있습니다.

숫자 8을 1의 보수부터 변경해 봅니다. ~(0000 1000) = 1111 0111 이 됩니다.

여기에 1을 더해줍니다. 1111 0111 + 0000 0001 = 1111 1000 (-8) 이 됩니다. 

 

위와 같이 양수를 음수로 변환하고 싶다면 2의 보수를 사용하여 빠르게 변환할 수 있습니다.

 

정말로 그런지 테스트용 코드와 결과를 보여드리겠습니다.

비교 대상은 숫자 하나를 -1을 곱하여 100억번, 2의 보수를 활용하여 100억번 반전하는 시간을 계산합니다.

#include <iostream>
#include <time.h>
using namespace std;

typedef unsigned long long uint64;

int main()
{
	uint64 Num = 1;
	time_t StartTime = time(nullptr);
	for (uint64 ii = 0; ii < 10000000000; ++ii)
	{
		Num *= -1;
	}
	cout << "* -1: " << time(nullptr) - StartTime << endl;

	StartTime = time(nullptr);
	for (uint64 ii = 0; ii < 10000000000; ++ii)
	{
		Num = (~Num) + 1;
	}
	cout << "bit: " << time(nullptr) - StartTime << endl;
}

모르는 문법이 있을 수 있습니다. 추후 배우니 우선 직접 코드를 돌려봐도 좋고 결과만 봐도 좋습니다.

결과 화면

결과 화면을 보면 -1을 100억번 반전하는데 곱했을 때 61초, 2의 보수를 활용하였을 때 28초 걸렸습니다.

100억번이라는 많은 횟수를 통하여 비트 연산이 컴퓨터에서는 더 빠른 것을 볼 수 있었습니다.

 

비트 단위 시프트 연산자

비트를 이동시키는 << (좌측 시프트 연산자), >> (우측 시프트 연산자)가 있습니다.

피연산자의 모든 비트열을 좌측 또는 우측으로 밀어내는 연산자입니다.

short Num = 1 << 2;
 
0000 0000 0000 0001
        << // 2 모든 비트열을 좌측으로 2칸 이동.
0000 0000 0000 0100


비트를 좌측으로 한칸 이동하는데
Num * 2와 같은 결과를 볼 수 있습니다.위 코드에서는 숫자 1의 비트열을 좌측으로 2칸 이동시켜 4가 됩니다.

short Num = 8 >> 2;
 
0000 0000 0000 1000
        >> // 8 모든 비트열을 우측으로 2칸 이동.
0000 0000 0000 0010


비트를 우측으로 한칸 이동하는데
Num / 2와 같은 결과를 볼 수 있습니다.위 코드에서는 숫자 8의 비트열을 우측으로 2칸 이동시켜 2가 됩니다.

>< 화살표 방향으로 비트를 얼마만큼 이동시키는 연산자입니다. 빠른 *2, /2 효과를 볼 수 있습니다.

 

사용시 주의할 점은 우측 또는 좌측 끝으로 값을 밀었다 당겼을 때 값이 복원되지 않는 점입니다.

예를 들어 4bit 데이터 1010 이 있다고 했을 때

1010 >> 2 = 0010 ← 1010을 우측으로 2번 밀어 0010으로 만든 후

0010 << 2 = 1000 ← 0010을 좌측으로 2번 밀었을 때 1000이 나옵니다.

비트 범위 밖으로 밀려난 값은 돌아오지 않습니다.

 

TODO: 비트 시프트 게임 관련 활용에 관한 내용 추가 예정.


축약 연산자

산술 연산비트단위 연산축약 연산(연산과 =의 조합)이 가능합니다.

int Num = 0;
Num += 10 // Num = Num + 10 을 축약
Num -= 5 // Num = Num - 5 를 축약
Num |= 1 // Num = Num | 1 을 축약
Num &= 1 // Num = Num & 1 을 축약

위 코드와 같이 Num += 10만 보자면 Num = Num + 10축약 연산자로 표현한 코드입니다.


증감 연산자

피연산자의 값을 1만 증가 또는 감소시키는 연산자입니다.

int Num = 10;
Num++; // 후위 증감, 11
Num--; // 후위 증감, 10
++Num; // 전위 증감, 11
--Num; // 전위 증감, 10

증감 연산자에는 전위 증감, 후위 증감 두가지가 있습니다.

아래의 코드와 그림을 보며 전위와 후위 증감의 차이점을 자세히 알아보겠습니다.

int Num = 10;
cout << Num++ << endl; // 10
cout << ++Num << endl; // 12

 

코드를 돌려보면 처음엔 10, 그 다음은 12를 출력하는 것을 볼 수 있습니다. 

왜 그럴까요? 코드의 흐름을 그림으로 보면서 알아봅니다.

후위 증감 연산자 동작 방식

후위 증감 연산자임시 메모리에 기존 값을 복사한 다음 복사된 값은 출력하고 기존 값은 나중에 증가됩니다.

 

 

전위 증감 연산자 동작 방식

전위 증감 연산자기존 값을 증가시키고 바로 사용합니다.

내부 구현 형태는 함수와 관련된 내용을 이야기할 때 보충하겠습니다.


혼자 해보기

1) 각 2진수를 10진수로 표현하세요.

0000 0001

0000 0010

0000 0100

0000 1000

0001 0000

0010 0000

0100 0000

1000 0000

 

 

2) 다음 signed 데이터 8bit 값을 10진수로 표현하세요. (부호 비트)

1111 1001

1111 1010 

 

 

3) 정수 하나를 입력 받아 값을 반전시켜 출력하세요. (비트 연산자만 사용, 10을 받으면 -10, -5를 받으면 5를 출력)

입출력 결과


이전편

2021.07.11 - [밑바닥부터 시작하는 게임 개발/C++] - [밑바닥 C++] 3. HP 등 값을 조작하는 연산자, 변하지 않는 값 상수

 

다음편

2021.08.16 - [밑바닥부터 시작하는 게임 개발/C++] - [밑바닥 C++] 5. 가위바위보 게임으로 알아보는 분기문(if)

 

 

 

반응형

댓글