수는 컴퓨터가 다루는 데이터의 종류(자료형) 중에서 가장 중요한 데이터로, 컴퓨터는 0과 1만 인식할 수 있기 때문에 사람과는 다른 방식으로 수를 표현합니다. 이번 클래스에서는 수를 표현 하는 다양한 방법과 수의 종류 중 하나인 정수에 대해 알아보고, 양의 정수와 음의 정수가 메모리에 어떻게 저장되는지도 함께 알아보겠습니다.
숫자를 표현하는 방법
수를 표현하는 방법을
기수법
이라고 하는데, 밑수를 정하면 밑수의 개수만큼의 숫자를 사용하여 수를 나타낼 수 있습니다. 일반적으로 우리가 일상생활에서 사용하는 숫자는 10진수로 나타내지만 컴퓨터에서는 2진수, 16진수 등 다양한 방법으로 나타낼 수 있습니다.
10진수
10진수는 0부터 9까지 총 열 개의 숫자로 모든 수를 표현합니다.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
2진수
2진수는 숫자 0과 1만 사용하며, 컴퓨터가 인식할 수 있는 표현법입니다.
0, 1
16진수
16진수는 수를 표현할 때 10개의 숫자와 여섯 개의 알파벳을 사용합니다.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
진수 변환
우선 10진수를 2진수로 변환하는 방법을 살펴보겠습니다. 수학을 이용해서 10진수를 2진수로 변환하기 위해서는 다음과 같이 다소 복잡하고 귀찮은 과정을 거쳐야 합니다.
예를 들어 10진수 25를 2진수로 변환하기 위해서는 우선 25를 2의 거듭제곱의 합으로 쪼갠 다음 25에서 가장 가까운 2의 거듭제곱수인 16(2
4)을 빼줍니다. 즉 25에서 16을 빼주면 9가 남습니다.
25 = 16 + 9
다음으로 이 9에 가장 근접한 2의 거듭제곱 수인 8(2
3)을 빼줍니다. 그러면 1이 남겠죠?
25 = 16 + 8 + 1 = 2
4
+ 2
3
+ 2
0
이제 2
2이나 2
1처럼 중간에 빠진 지수는 0으로 표현해줍니다.
25 = 1 x 2
4
+ 1 x 2
3
+ 0 × 2
2
+ 0 X 2
1
+ 1 x 2
0
이렇게 나열된 표현식에서 2의 거듭제곱 수를 제외하고 앞의 수인 1과 0만 모아 나열하면 2진수로 표현된 숫자 25를 알아낼 수 있습니다.
1 x 2
4
+ 1 x 2
3
+0 × 2
2
+ 0 × 2
1
+ 1 × 2
0
= 11001
하지만 프로그래밍을 하는데 이렇게 일일이 계산을 해서 코드를 입력할 수는 없겠죠? 파이썬은 다음과 같이 함수를 사용하여 10진수를 2진수로 간단하게 변환할 수 있습니다.
a1 a2 a3 a4 ... b1 b2 b3 b4 ... c1 c2 c3 c4 ...
bin(25)
파이썬의 bin() 함수는 정수를 2진수로 표현하는 함수로 결과 값 앞에 나오는 0b는 2진수를 의미하는 binary를 뜻합니다.
파이썬에서 10진수를 16진수로 변환하는 방법도 간단합니다. hex() 함수를 사용하면 됩니다. 다음과 같이 8비트 컴퓨터 메모리의 주소를 hex() 함수로 변환하면 0x2d라는 값이 나옵니다.
'0b11001'
address = 0b00101101 hex(address)
hex() 함수는 정수를 16진수로 표현합니다. 위의 예와 같이 2진수로 8비트를 표현하기 위해서는 여덟 자릿수가 필요하지만, 16진수로 표현하면 두 자릿수로 간단히 나타낼 수 있어 가독성을 높일 수 있기때문에 메모리 주소를 나타낼 때는 16진수를 사용합니다. 참고로 32비트 컴퓨터도 서른 두 자릿수의 2진수 대신 여덟 자릿수의 16진수로 표현합니다.
'0x2d'
address = 0b10010001101001010101111001101 hex(address)
양의 정수
컴퓨터는
정수를 1바이트, 2바이트, 4바이트, 8바이트 등 다양한 크기로 저장하는데, 정수에는 양수와 음수가 있기 때문에 부호를 나타내기 위해 1비트를 사용합니다. 즉 저장된 숫자에 맨 앞의 비트가 0이면 양수이고 1이면 음수입니다.
예를 들어 25를 메모리에 1바이트의 크기로 저장하는 경우 맨 앞의 비트는 양수이므로 0이 되고, 10진수 25를 2진 수로 변환하면 11001
이기 때문에 나머지 비트를 0으로 채우면 메모리에 다음과 같이 저장됩니다.
맨 앞의 비트가 부호를 나타낸다는 것만 제외하면 10진수를 2진수로 변환한 것과 같으며, 컴퓨터에서 1바이트로 나타낼 수 있는 수의 범위는 -128 ~ 127까지 입니다. 이는 정수에는 음수가 포함되 고 맨 앞의 비트를 부호로 사용하기 때문에 표현할 수 있는 양수의 범위가 절반으로 줄어들기 때문이며, 음수를 취급하지 않는 정수 자료형의 경우에는 0 ~ 255까지의 범위를 숫자로 나타낼 수 있습니다.
음의 정수
컴퓨터는 음수를 표현하기 위해
보수를 사용합니다. 즉 컴퓨터가 음수를 보수의 형태로 저장하기 때문에 컴퓨터에서 음의 정수가 어떻게 저장되는지 이해하기 위해서는 보수의 개념을 알아야 할 필요가 있습니다.
보수
보수란 쉽게 말해 보충해 주는 수를 말합니다. 예를 들어 10진수에서 9의 보수를 구하는 경우, 3의 9 보수는 3을 더해 9가 되는 수인 6입니다. 26의 9 보수는 뭘까요? 73입니다. 이렇게 9의 보수를 구하는 경우라면 어떤 수의 각 자릿수를 9에서 빼면 9의 보수를 구할 수 있습니다. 3의 10 보수는 3의 9 보수에 1을 더한 7이 되고 26의 10 보수는 26의 9 보수에 1을 더한 74가 됩니다. 원리를 알면 간단합니다.
2의 보수
2의 보수는 컴퓨터가 음수를 표현할 때 사용하는 방법입니다. 2의 보수를 알아내기 위해 우선 2진수의 1의 보수를 구해보겠습니다. 2진수의 1의 보수를 구하는 것도 앞에서 살펴본 보수를 구하는 방법과 같습니다. 2진수에서 1의 보수를 구하기 위해서는 1에서 각 자릿수의 수를 빼면 됩니다.
1010의 1의 보수 = 0101
이제 1010의 2의 보수를 구해 볼까요? 1010의 1의 보수인 0101에 1을 더해주면 됩니다. 즉, 1010의 2의 보수는 0110이 됩니다.
음수 표현
컴퓨터는 음수를 2의 보수로 표현합니다. 예를 들어 음수 -4는 컴퓨터에서 다음과 같이 표현됩니다.
이미지와 같이 컴퓨터는 4를 2진수(0000 0100)로 변환한 다음 1의 보수를 구하고, 1의 보수인 1111 1011에 1을 더해 2의 보수를 구하여 1111 1100으로 표현합니다. 즉, 컴퓨터가 표현하는 -4는 1111 1100인 것이죠.
-4가 1111 1100인지 파이썬으로 확인해보겠습니다. 아래의 코드는 -4라는 정수를 컴퓨터 메모리에 저장되는 ‘바이트’
형태로 표현하는 코드입니다.
'0x1234abcd'
(-4).to_bytes(1, byteorder='little', signed='true')
코드의 첫 번째 인자는 몇 바이트로 나타낼 것인지를 지정하고 두 번째 인자는 빅 엔디언인지 리틀 엔디언인지를 정하는 바이트 오더 인자입니다. 세 번째 인자는 양수와 음수를 모두 표현할지, 양수만 표현할지를 정하는 인자이며 출력 값은 16진수인 xfc로 표현됩니다.
16진수인 0xFC를 2진수로 변환해보면 1111 1100이 됩니다. 앞에서 구해본 2의 보수와 같죠? 이렇게 컴퓨터는 음수를 2의 보수를 이용해 저장합니다. 그런데 컴퓨터는 왜 음수를 2의 보수로 저장할까요?
첫째, 양수와 음수를 모두 양수처럼 저장한다면 0000 0000과 1000 0000은 +0과 -0이 됩니다. 즉, 0을 표현하는 두 가지 방법이 존재하게 되어 컴퓨터 입장에서는 비트 하나를 낭비하는 것이 됩니다. 또한 CPU에서 뺄셈을 하는 경우 +0과 -0을 비교하게 되면 결과 값이 예상과 다르게 나올 수도 있죠.
둘째, 컴퓨터에서 정수의 덧셈은 단순히 두 수를 더하면 되지만, 뺄셈은 2의 보수를 활용합니다. 즉 9 – 4를 계산한다면 9에서 4를 빼는 것이 아니라 9와 -4를 더하는 것이죠.
위 이미지의 계산 결과는 받아올림이 발생하여 1 0000 0101이 나오지만, 받아올림 수 1은 버리는 수로 최종 결과는 0000 0101, 즉 5로 9 – 4가 잘 계산되었음을 알 수 있습니다.