
컴퓨터에서 숫자를 표현하는 방법: 정수
아날로그 세계의 데이터를 디지털 세계로 옮기려면?
컴퓨팅 기술이 급격하게 발전하고, 사회 곳곳에 컴퓨터가 쓰이기 시작하면서, 세상에 존재하는 아날로그 데이터를 디지털 데이터로 변환해야 할 필요성이 증가했다.
현실에 존재하는 데이터는 아날로그(Analog) 데이터다. 소리, 온도, 색깔과 같은 아날로그 데이터는 실수의 성질을 가지는 연속적인 값을 가진다. 반면 디지털 데이터의 값은 정수의 성질을 가져서 특정 구간마다 끊겨 표현된다. 오늘날의 컴퓨터는 여러 공학적·실용적 이유로 0과 1을 이용하는 2진수 디지털 시스템을 이용하고 있다.
만일 우리가 4가지의 음식(햄버거, 피자, 치킨, 떡볶이)을 2개의 비트로 표현하고 싶다고 생각해 보자. 각 비트는 0 혹은 1의 값을 가질 수 있으니까, 2개의 비트로 표현할 수 있는 상태는 00, 01, 10, 11로 네 가지이다. 각각의 음식을 각각의 비트 상태와 매칭하면 우리는 4가지 종류의 음식을 2개의 비트를 이용해 표현할 수 있다.
이처럼 컴퓨터 시스템의 “정보”는 “비트”와 “문맥”의 조합으로 이루어진다. (정보=비트+문맥) 만일 우리가 N개의 비트를 가지고 있다면, 우리는 2N개의 상태를 만들 수 있다. 수신자와 발신자가 약속을 하고 2N개에 달하는 각각의 상태마다 특정한 의미를 부여한다면, 우리는 N개의 비트를 주고받으면서 정보를 교환할 수 있을 것이다.
컴퓨터에서 이용하는 두 가지 정수 표현법
컴퓨터에는 크게 두 가지 종류의 정수 표현법이 있다.
첫 번째는 부호가 없는 정수(unsigned integer)이다. 부호가 없는 정수에서는 N개의 비트를 모두 절대값을 나타내는 데 이용한다.
두 번째는 부호가 있는 정수(signed integer)이다. 부호가 있는 정수에서는 1개의 비트는 부호를, 나머지 비트는 절댓값을 나타내는 데 이용한다.
unsigned integer의 표현 방법
먼저 부호 없는 정수에 대해 생각해 보자.
0부터 15까지의 수에 대해서 생각해 보자. 0부터 15까지의 모든 수는 20, 21, 22, 23을 더하거나 더하지 않음으로써 만들어 낼 수 있다. 예를 들면 10은 21+23과 같이 표현할 수 있다. 11은 20+21+23과 같이 표현할 수 있다. 각각의 거듭제곱 꼴을 더했는지의 여부를 비트로 나타낸다면, 우리는 4개의 비트를 이용해 0부터 15까지의 수(16개)를 나타낼 수 있을 것이다. 예를 들면 10은 1010, 11은 1011과 같이 나타낼 수 있다.
이제 0부터 2N-1까지의 수에 대해 생각해 보자. 0부터 2N-1까지의 수는 20, 21, 22, …, 2N-1를 더하거나 더하지 않음으로써 만들어 낼 수 있다. i번째 비트에 2i를 더했는지의 여부를 저장한다면, 우리는 N개의 비트를 이용해 0부터 2N-1까지의 수(2N개)를 나타낼 수 있다.
signed integer의 표현 방법
부호가 없는 정수의 표현법을 응용해 부호가 있는 정수의 표현법을 생각할 수 있다. 부호가 있는 정수를 표현하는 방법에는 크게 3가지 방법이 제안되었다.
방법 1. 부호 비트 이용하기
첫 번째 방법은 부호 비트를 이용하는 것이다. 정수에 대한 데이터는 결국은 “부호”와 “절댓값”의 조합으로 이루어져 있다고 생각할 수 있다. 이때 1개의 비트는 부호를 나타내는 데 쓰고, 나머지 비트는 절댓값을 나타내는 데 사용해 정수를 나타낼 수 있다.
N개의 비트가 있다고 생각해 보자. 맨 앞에 오는 비트는 숫자의 부호를 저장한다. 값이 0이면 양수, 1이면 음수다. 나머지 N-1개의 비트를 이용해 절댓값을 나타낸다. 절댓값의 범위는 0~2N-1-1 사이의 정수이다. 따라서 우리는 N개의 비트를 이용해 -(2N-1-1)부터 (2N-1-1)까지의 값을 나타낼 수 있다.
이 방법은 이해하기 편리하지만, 여러 실용적인 문제를 가지고 있다.
- 0을 나타내는 방법이 두 개다 (00…000, 10…000)
- 덧셈 연산을 수행하는 방법이 번거롭다. 음수와 양수의 덧셈을 고려했을 때, 덧셈 연산을 위해서 뺄셈 연산까지 따로 구현해야 한다.
방법 2. 1의 보수법
두 번째 방법은 1의 보수법을 이용하는 것이다. 1의 보수법은 해당 양수의 모든 비트를 반전하여 음수를 표현하는 방법이다. 즉, (-1)의 표현법은 (+1)의 표현법에 NOT 연산을 적용한 값과 같다. 1의 보수법에서, (+x)과 (-x)를 더하면 111…1이 된다.
1의 보수로 계산을 하는 경우, 자리 올림이 발생할 때 그 값을 결과값에 더해 계산하면 된다.
이처럼 1의 보수로는 덧셈 연산을 하기가 훨씬 용이하지만, 2개의 0 표현법(0000, 1111)이 있다는 단점은 여전히 해결되지 않았다.
방법 3. 2의 보수법
세 번째 방법은 2의 보수법을 이용하는 것이다. 2의 보수법은 해당 양수의 모든 비트를 반전한 후 1을 더해 음수를 표현하는 방법이다. 2의 보수법에서 (+x)과 (-x)를 더하면 자리 올림이 발생하면서 000…0이 된다.
2의 보수로 계산을 하는 경우, 자리 올림이 발생할 때 그 값을 버리고 계산을 진행하면 된다.
정수 비트의 확장
n개의 비트로 표현된 정숫값이 있다고 하자. 그런데 모종의 이유로 이를 n+k개의 비트로 표현하고 싶다고 생각하자. 이러한 상황은 컴퓨터 프로그래밍에서 자주 발생한다. 다음 상황에 대해 생각해 보자.
unsigned int a = 6; | int a = -5; |
unsigned long b = long(a); | long b = long(a); |
unsigned integer에서는 정수의 부호를 고려하지 않는다. 따라서 unsigned integer로 표현된 정수의 비트를 확장하고 싶으면, 그냥 앞에 0을 계속 추가하면 된다.
반면 signed integer에서는 정수의 부호를 고려해야 한다. 이때는 새롭게 추가되는 k개의 비트를 맨 앞에 있는 비트값으로 채우고, 나머지 n개의 비트는 기존 값을 그대로 옮기면 된다.
2의 보수법 그림으로 이해하기
왜 2의 보수법을 이용하면 덧셈 연산이 편해질까? 이것은 2의 보수법이 시계와 같은 구조를 가지고 있기 때문이다. 8칸짜리 원형 다이얼이 있다. 다이얼에 붙은 숫자는 0에서 시작해 시계 방향으로 1씩 커진다. (주황색 숫자) 다이얼은 시계 방향으로만 돌아간다. 이때 다이얼을 반시계 방향으로 3칸 돌리고 싶다면, 어떻게 해야 할까? 다이얼을 시계 방향으로 5칸, 즉 (8-3)칸 회전시키면, 반시계 방향으로 3칸 회전시키는 것과 같은 효과를 낼 수 있다.
이제 회전의 방향에 의미를 부여해 보자. 시계 방향 회전의 부호는 +이다. 반시계 방향 회전의 부호는 -이다. 0번 칸에는 숫자 (+0)을 할당한다. 1번부터 3번 칸에는 숫자 (+1), (+2), (+3)을 순서대로 할당한다. 반시계 방향으로 1칸을 돌리려면, 시계 방향으로 7칸을 돌려야 한다. 따라서 숫자 (-1)은 7번 칸에 할당한다. 숫자 (-2)는 6번 칸에, (-3)은 5번 칸에, (-4)는 4번 칸에 할당한다.
(+2) + (-3) 연산을 직접 실행해 보자. 보라색 점은 원점 0에서 출발한다. (+2)를 하기 위해 보라색 점은 시계 방향으로 2칸 회전한다. 다시 (-3)을 하기 위해 보라색 점은 시계 방향으로 5칸 회전한다. 이는 반시계 방향으로 3칸 회전하는 것과 같다. 최종적으로 보라색 점은 시계 방향으로 7번 회전한다. 즉, 반시계 방향으로 1칸 회전한 셈이다. 이것은 (-1)의 위치이다. 회전 연산을 통해서 우리는 성공적으로 실수 연산을 수행한 것이다.
Overflow
이제 Overflow에 대해 알아보자. 그림에서 양수는 핑크색 영역, 음수는 초록색 영역에서 표현된다. 보라색 점은 원점 0에서 출발한다. 음수와 양수 사이에 덧셈 연산을 하면 보라색 점은 시계 방향으로 움직였다가 다시 반시계 방향으로 이동한다. 이 과정에서 보라색 점은 빨강색 Overflow 라인을 지나치지 않는다. 하지만 지나치게 큰 양수끼리 덧셈을 하면 연산 과정에서 보라색 점이 핑크색 영역을 넘어가 초록색 영역으로 옮겨간다. 반대로 지나치게 큰 음수끼리 덧셈을 하면 연산 과정에서 보라색 점이 초록색 영역을 넘어가 핑크색 영역으로 옮겨간다. 이것이 Overflow이다. 다르게 표현하자면, 두 부호가 같은 정수의 덧셈 결과 맨 첫 번째 비트의 값이 바뀌었다면, Overflow가 일어난 것이다.
최근 게시물