이번 시간에는 컴퓨터가 0과 1로 숫자를 표현하는 방법에 대해 알아보는 시간을 가져보자. 이미 알고 있는 분들도 있을 수 있겠지만 컴퓨터는 사람과 달리 기본적으로 0과 1밖에 이해하지 못한다. 여기서 0과 1이라고 하는 것은 컴퓨터가 주고받는 전기신호의 강도가 약하면 0, 강하면 1로 인식하게 된다. 그런데 0과 1밖에 이해하지 못하는 컴퓨터가 어떻게 3 + 4 혹은 그 이상의 복잡한 연산을 수행할 수 있는 걸까? 이 질문의 답에 대해 알아보는 시간이 오늘 포스팅의 핵심 주제이다.
정보 단위
먼저 컴퓨터가 이해하는 정보 단위에 대해 알아보자. 컴퓨터가 이해하는 가장 작은 정보 단위를 비트(bit)라고 한다. 위의 이미지와 같이 전구가 꺼지면 0, 켜지면 1로 이해하는 것이다. 그리고 0 혹은 1이냐에 대한 하나의 정보를 담는 가장 작은 단위가 비트인 것이다. 그래서 1 비트로는 두 가지(0 혹은 1)의 정보를 표현할 수 있다.
위의 이미지는 2비트와 3비트로 표현할 수 있는 정보의 경우의 수를 보여준다. 2비트는 (0,0), (0,1), (1,0), (1,1)을 표현할 수 있으며 3비트는 (0,0,0), (1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1), (0,1,1), (1,1,1)을 표현할 수 있다. 그래서 2비트로는 4종류의 정보를 표현할 수 있고 3비트로는 8종류의 정보를 표현할 수 있다. 비트가 증가할수록 2의 n제곱 단위로 표현할 수 있는 정보가 늘어난다.
n비트로 2^n가지의 정보 표현 가능
프로그램은 수많은 비트로 이루어져 있음
다만, 보통 "이 파일은 8,920,120 비트야"라고 표현하지 않음
비트보다 더 큰 단위를 사용
바이트, 킬로바이트, 메가바이트, 기가바이트, 테라바이트
1바이트(1byte)
8비트(8bit)
1킬로바이트(1kB)
1,000바이트(1000byte)
1메가바이트(1MB)
1,000킬로바이트(1000kB)
1기가바이트(1GB)
1,000메가바이트(1000MB)
1테라바이트(1TB)
1,000기가바이트(1000GB)
위의 표를 보고 이런 질문을 하시는 분이 있을 거라 생각한다. "이전 단위를 1,000개씩 묶은 단위인가? 1,024개가 아니고?". 실제로 일부 문서를 보면 1,024개로 혼용해서 표현하는 문서들도 있다. 하지만 엄밀히 따지면 1,024개로 묶는 단위는 kiB, MiB, GiB... 와 같이 단위가 따로 존재한다. 하지만 과거에는 정보의 단위가 크지 않기 때문에 1,000개씩 묶으나 1,024개씩 묶으나 별 큰 차이가 없었기 때문에 혼용해서 사용하곤 하였다. 그렇지만 최근에는 다루는 정보의 크기가 방대하기 때문에 1,000개와 1,024개의 차이는 마치 눈덩이가 불어나듯이 정말 큰 정보의 차이로 이어지게 되었다. 그래서 1,000개씩 묶는 단위와 1,024개씩 묶는 단위를 구분해서 작성하는 추세이다.
워드(word)
추가적으로 우리가 컴퓨터구조를 학습하는 데 있어서 워드라고 하는 조금 중요하게 다뤄지는 단위가 있다. 워드는 CPU가 한 번에 처리할 수 있는 정보의 크기 단위를 의미한다.
하프 워드(Half word) : 워드의 절반 크기
풀 워드(Full word) : 워드 크기
더블 워드(Double word) : 워드의 두 배 크기
예를 들어, 현재 필자의 컴퓨터의 CPU가 한 번에 32bit씩 처리할 수 있다면 필자 컴퓨터의 1 word는 32bit인 셈이다.
이진법(binary)
이제 본격적으로 0과 1로 숫자를 표현하는 방법에 대해 알아보자. 수학적으로 0과 1로만 숫자를 표현하는 방법을 이진법이라고 한다.
0과 1로 숫자를 표현하는 방법
숫자가 1을 넘어가는 시점에 자리 올림
우리가 일상적으로 사용하는 진법은 숫자가 9를 넘어갈 때 자리올림하는 십진법
이진법을 위의 그림을 통해서 한 번 자세히 알아보자. 일단 그림에서 왼쪽에 있는 게 우리가 일상에서 흔히 사용해서 익숙한 십진법이다. 그리고 오른쪽은 컴퓨터 언어인 이진법이다. 숫자가 1을 넘어가는 시점에 자리 올림을 하는 특징이 있다. 그럼 이진수를 어떻게 십진수로 눈으로 보고 읽어야 되는지 살펴보자.
값
512
256
128
64
32
16
8
4
2
1
위치
0
0
0
1
0
1
0
0
1
0
만약 위의 표처럼 10자리 수의 이진수 0001010010이 있다고 가정해 보자. 그럼 오른쪽에서부터 값은 1이고 두 번째는 2이다, 그리고 그 뒤로 2배씩 증가한다고 보면 된다. 그리고 해당 위치가 0이면 값을 더하지 않고, 1일 경우만 값을 더하면 된다. 그렇다면 위의 표 같은 경우 현재 1인 값들은 2, 16, 64이다. 이들을 모두 더하면 82가 된다. 즉 이진수 0001010010은 십진수로 82인 것이다. 앞서 보았던 1~9까지의 이진수도 다음과 같은 방식으로 계산해 보면 오른쪽 그림과 같은 결과로 나온다는 걸 확인할 수 있다.
(표에는 512까지만 작성하였는데 왼쪽으로 갈수록 계속 2배씩 증가된다고 보면 된다. 1024, 2048...)
그래서 0과 1밖에 이해하지 못하는 컴퓨터한테 숫자를 알려주려면 우리가 일상적으로 사용하는 십진수가 아니라 이진수로 알려주어야 한다.
하지만 위와 같이 10 + 1000으로만 표현하게 된다면, 이 숫자가 십진수 10 + 1000인지, 이진수 10 + 1000인지 헷갈릴 수 있는 상황이 발생한다. 그렇기 때문에 이진수를 표기하는 방법을 아래와 같이 크게 두 가지가 있다.
1000₍₂₎
0b1000
두 번째 방법 같은 경우는 보통 프로그래밍을 하면서 많이 사용하는 방식이다.
이진수로 음수 표현
지금까지 이진수에 대해 간단히 알아보았지만, 이진수로 음수를 표현하는 방법도 한 번 알아보자. 컴퓨터 내부에서 이진수를 이용해서 음수를 어떻게 표현하는지는 매우 중요하다. 왜냐하면 우리가 보통 십진수로 음수를 표현한다고 하면 그저 숫자 앞에 - 기호를 추가해 주면 되지만 컴퓨터는 0과 1밖에 인식하지 못한다. 즉, - 부호를 이해하지 못하기 때문에 우리가 십진수로 음수를 표현하는 방식과 같은 방법을 사용할 수 없다. 그래서 0과 1만으로 음수를 표현하는 방법이 다양하게 만들어졌는데 그중에서도 가장 널리 사용되는 방법이 2의 보수법이다.
그렇다면 2의 보수는 무엇일까? 먼저 2의 보수에 대한 정의를 찾아보면 다음과 같다. "어떤 수를 그보다 큰 2^n에서 뺀 값"이다. 바로 예시를 통해 한 번 알아보자. 이진수 11₍₂₎ 과 같은 이진수가 있다고 했을 때, 이 숫자를 음수로 한 번 표현해 보자. 그럼 정의에서 "어떤 수"는 현재 11₍₂₎가 되는 것이고, "11₍₂₎를 그보다 큰 2^n에서 뺀 값"으로 해석할 수 있다. 그럼 11₍₂₎ 보다 큰 2^n은 무엇일까? 이 말은 곧 11₍₂₎보다 바로 한 단계 큰 수는 무엇인가? 와 같다고 보면 된다. 11₍₂₎보다 한 단계 바로 큰 수는 100₍₂₎이다.11₍₂₎을 십진수로 변환하면 3, 100₍₂₎을 십진수로 변환하면 4이다.(*이진수 → 십진수로 변환하는 방법이 아직도 헷갈린다면 위의 글을 다시 참고하자.)
그럼 정의를 다시 한번 살펴보자. "어떤 수를 그보다 큰 2^n에서 뺀 값"이니까 해당 정의에 우리가 방금 구한 값들을 대입시키면 된다. 그럼 "11₍₂₎를 그보다 큰 100₍₂₎에서 뺀 값"이 된다. 따라서 100₍₂₎ - 11₍₂₎은 01₍₂₎이 되므로 11₍₂₎를 음수로 표현하면 01₍₂₎이 된다.
하지만 필자는 처음 저 정의를 들었을 때 도무지 무슨 말인지 이해가 잘 가지 않았다. 그래서 필자의 방식대로 좀 더 쉽게 정의를 하자면 "모든 0과 1을 뒤집고 1을 더한 값"으로 생각하면 이해가 조금 쉬울 것이다. 11₍₂₎이 있을 때, 0은 1로, 1은 0으로 먼저 뒤집어준다. 그럼 00₍₂₎이 되고 거기에 1을 더한 값이니 01₍₂₎이 된다. 둘 중 어떤 정의로 외우는지는 상관없이 본인이 기억하기 편한 방법을 선택하면 된다.
여기까지 배웠을 때, 이런 의문이 들 수 있다. 01₍₂₎이 십진수 1을 표현하기 위한 양수의 01₍₂₎인지 혹은 11₍₂₎이 음수로 표현된 01₍₂₎인지는 어떻게 구분할까?
일단 겉모습만 봤을 때는 구분이 안될 수밖에 없다. 하지만 CPU 내부에 있는 레지스터에 플래그 레지스터라고 하는 특별한 레지스터가 있다. 그 플래그 레지스터에 "현재 값은 양수이다." 혹은 "현재 값은 음수이다"라고 표기를 하게 된다. 그 말인즉슨, 모든 숫자가 컴퓨터 내부에서 플래그 값을 가지고 있다고 생각하면 된다.
16진법
이진법을 통해서 컴퓨터가 이해할 수 있는 0과 1로만 숫자를 표현할 수 있게 되었지만 사람이 읽기에는 숫자가 너무 길어진다는 단점이 있다. 예를 들어, 십진수로 32는 이진수로 100000₍₂₎으로 표현된다. 그래서 컴퓨터의 데이터를 표현할 때 16진법도 많이 사용한다. 16진법은 수가 15가 넘어가는 시점에 자리올림을 한다.
위에 표를 보면 16진법에서는 십진수 10을 A라고 표현하고, 그 뒤부터 11 = B, 12 = C, 13 = D, 14 = E, 15 = F로 표현되고 있다. 그리고 15를 넘어가는 시점에 자리올림을 해서 10이 된다. 개인적으로 필자는 16진법을 외울 때 다음과 같은 방법을 사용하였다. 먼저 주먹을 쥔다. 손가락이 아직 한 개도 안 펴졌으니 A, 그리고 한 개를 피면 B, 손가락이 1개 펴졌으니 11, 그리고 두 개를 피면 C이고 12라고 쉽게 기억할 수 있다. 마찬가지로 마지막 손가락까지 전부 펴졌다면 F이고 15이다.
16진법 또한 십진수 16이 16진법으로 10으로 표현된다는 걸 알 수 있다. 그렇다면 이전 이진법과 마찬가지로 10이 십진수의 10인지, 2진수의 10인지, 16진수의 10인지 알 수가 없다. 그래서 16진법 표기방법이 있다. 예를 들어 16진수 15를 표기하는 방법은 아래와 같다.
15₍₁₆₎
0x15
16진수 ➡ 이진수 변환
16진수에 이진수로 변환하는 방법을 알아보기 전에 먼저 한 자리의 16진수는 4 비트라는 점을 기억하자. 우리가 이진수를 처음 배웠을 때 1,2,4,8.. 과 같이 한 자릿수당 2배로 증가한다는 걸 배웠다. 그래서 한 자리의 16진수를 표현하기 위해서는 4비트(2^4)가 필요하다는 것이다. 그럼 이제 16진수를 2진수로 변환하는 방법을 살펴보자.
16진수를 2진수로 변환할 때는 위의 그림에서 보다시피 각 자릿수를 2진수로 변환 후, 모두 이어주면 된다. 16진수의 각 한자리가 이진수 4비트로 구성되어 있는 걸 확인할 수 있다.
반대로 2진수를 16진수로 변환하는 과정도 위와 같이 각 4비트의 이진수를 16진수로 변환한 뒤 변환된 16진수를 모두 이어주면 된다. 그림에서 보면 알 수 있다시피 16진수와 2진수간에 변환은 매우 쉽기 때문에 많이 사용되는 거 같다.