진법은 수를 몇 가지 숫자로 적느냐로 나뉩니다. 2진법은 0과 1, 8진법은 0~7, 16진법은 0~9와 A~F(10~15)를 씁니다. 컴퓨터는 모든 값을 0과 1의 비트(bit)로 담고, 사람은 긴 2진수를 짧게 읽으려고 4비트씩 묶은 16진수와 3비트씩 묶은 8진수를 씁니다. 진법을 오가는 계산은 메모리 주소·IP 주소·해시 문제에 다시 나옵니다. 아래 그림은 10진수 26을 2로 나눈 나머지가 2진수가 되고, 비트를 넷씩·셋씩 묶어 16진수·8진수가 되는 모습입니다.
110진수를 2진수로 — 나머지를 거꾸로
10진수를 몫이 0이 될 때까지 2로 나누고, 나머지를 아래에서 위로 읽으면 2진수입니다.
| 나눗셈 | 몫 | 나머지 |
|---|---|---|
| 26 ÷ 2 | 13 | 0 |
| 13 ÷ 2 | 6 | 1 |
| 6 ÷ 2 | 3 | 0 |
| 3 ÷ 2 | 1 | 1 |
| 1 ÷ 2 | 0 | 1 |
처음 나온 나머지가 맨 오른쪽 자리라 26은 11010입니다. 8진수·16진수도 8·16으로 나눠 나머지를 거꾸로 읽습니다. 어느 진법인지 밝힐 때는 (11010)₂처럼 밑을 작게 붙여 적습니다.
2자리 값 — n진수를 10진수로
오른쪽 끝이 1의 자리이고, 왼쪽으로 갈 때마다 밑(2·8·16)을 한 번씩 곱한 값이 자리 값입니다. 숫자마다 자리 값을 곱해 더하면 10진수가 됩니다.
| 진법 | 자리 값(왼쪽으로 갈수록 큼) | 예 |
|---|---|---|
| 2진수 | … 16 · 8 · 4 · 2 · 1 | 11010 → 16 + 8 + 2 = 26 |
| 8진수 | … 512 · 64 · 8 · 1 | 123 → 64 + 16 + 3 = 83 |
| 16진수 | … 4096 · 256 · 16 · 1 | 1A → 16 + 10 = 26 |
8진수 123을 83으로 바꾸는 계산이 이진 탐색과 해싱의 기수 변환법입니다.
3넷씩 묶어 16진수, 셋씩 묶어 8진수
16은 2⁴, 8은 2³이라, 2진수를 오른쪽 끝부터 넷씩 묶으면 16진수 한 자리, 셋씩 묶으면 8진수 한 자리가 됩니다. 모자라는 왼쪽 자리는 0으로 채웁니다.
- 11010 → 0001 1010 → 1과 10(A) → 16진수 1A
- 11010 → 011 010 → 3과 2 → 8진수 32
거꾸로 16진수는 한 자리를 2진수 네 자리로 풉니다. 3C는 0011 1100이고, A~F는 1010~1111입니다. 16진수끼리 더하다 한 자리가 16이 되면 그 자리에 0을 쓰고 윗자리로 1을 올립니다. 1C + 4는 C(12) + 4 = 16이라 20입니다.
4n비트로 나타내는 값 — 2ⁿ가지
비트 하나는 0과 1 두 가지라, n비트로는 0부터 2ⁿ − 1까지 2ⁿ가지 값을 나타냅니다.
| 비트 수 | 값의 수 | 쓰는 곳 |
|---|---|---|
| 4 | 2⁴ = 16 | 16진수 한 자리 |
| 8 | 2⁸ = 256 | 1바이트, IPv4 주소 한 칸(0~255) |
| 10 | 2¹⁰ = 1,024 | 크기 1,024인 페이지의 오프셋 |
| 16 | 2¹⁶ = 65,536 | IPv6 주소 한 칸(16진수 네 자리) |
거꾸로 값 n가지를 가리려면 2ᵏ이 n 이상인 가장 작은 k비트가 있어야 합니다. 100가지는 2⁷ = 128이라 7비트입니다. 가상 메모리: 페이징과 세그먼테이션의 '아래 10비트'와 IP 주소와 IPv6의 16진수 칸이 이 셈입니다.
5C에서 진법 쓰기
C는 0x로 시작하는 상수를 16진수, 0으로 시작하는 상수를 8진수로 읽고, printf의 %o·%x·%X로 8진수·16진수를 찍습니다.
#include <stdio.h>
int main(void) {
int n = 26;
printf("%o %x %X\n", n, n, n);
printf("%d %d\n", 032, 0x1A);
return 0;
}
32 1a 1A
26 26%x는 앞에 0x를 붙이지 않습니다. 010은 10이 아니라 8진수 10, 곧 8입니다.
예제
10진수 200을 2진수·16진수·8진수로
| 단계 | 한 일 | 결과 |
|---|---|---|
| 1 | 200을 2로 나눈 나머지를 거꾸로 읽기 | 11001000 |
| 2 | 넷씩 묶기 — 1100 · 1000 | 16진수 C8 |
| 3 | 셋씩 묶기 — 011 · 001 · 000 | 8진수 310 |
| 4 | 자리 값으로 확인 — 128 + 64 + 8 | 200 |
Python으로 맞춰 봅니다.
n = 200
print(bin(n), oct(n), hex(n))
0b11001000 0o310 0xc8같은 코드를 Java·Python으로
| C | Java | Python | |
|---|---|---|---|
| 8진수 상수 | 032 | 032 | 0o32 — 032는 문법 오류 |
| 2진수 상수 | C23부터 0b11010 | 0b11010 | 0b11010 |
| 8진수·16진수로 찍기 | %o · %x | printf의 %o · %x | oct(26)이 '0o32', hex(26)이 '0x1a' |
| 16진수 글자를 수로 | — | Integer.parseInt("1A", 16) | int('1A', 16) |
시험 포인트
- 필기: 2·8·10·16진수를 서로 바꾸는 문제가 나옵니다. 10진수는 밑으로 나눈 나머지를 거꾸로 읽고, 2진수를 넷씩 묶으면 16진수, 셋씩 묶으면 8진수입니다. A~F는 10~15이고, n비트는 2ⁿ가지(2¹⁰ = 1,024)입니다.
- 실기: 계산 결과를 문제가 정한 진법으로 씁니다(포인터와 16진수 출제 주제). 16진수 주소에 바이트 수를 더할 때는 16에서 올림합니다. C 코드에서는
0x·0으로 시작하는 상수와%x·%o출력을 그대로 읽습니다.
확인 문제
1. 서로 다른 값 300가지를 비트로 가리려면 적어도 몇 비트가 있어야 하나요?
- ① 8비트
- ② 9비트
- ③ 10비트
- ④ 300비트
정답
②입니다. 2⁸ = 256은 300보다 작고, 2⁹ = 512는 300 이상입니다.
- ① 256가지까지만 가립니다. 44가지가 남습니다.
- ③ 가릴 수는 있지만 가장 적은 비트가 아닙니다. 9비트로 됩니다.
- ④ 값 하나에 비트 하나를 쓰는 셈입니다. n비트는 2ⁿ가지를 가립니다.
2. 10진수 44를 2진수, 16진수, 8진수로 차례로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
정답
받는 답: 2진수 101100 · 16진수 2C · 8진수 54
44를 2로 나누면 나머지가 0, 0, 1, 1, 0, 1 차례로 나오고, 거꾸로 읽어 101100입니다. 넷씩 묶으면 0010 1100이라 2C, 셋씩 묶으면 101 100이라 54입니다.
- 틀리는 꼴 001101: 나머지를 위에서부터 읽었습니다. 처음 나온 나머지가 맨 오른쪽입니다.
- 틀리는 꼴 16진수 B0: 왼쪽부터 넷씩 묶었습니다(1011 00). 묶음은 오른쪽 끝부터입니다.
3. 16진수 B7을 2진수로 풀어 10진수로 바꾸는 표입니다. (가)·(나)를 채우세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
| 16진수 자리 | B | 7 |
|---|---|---|
| 10진수 값 | 11 | 7 |
| 2진수 네 자리 | (가) | 0111 |
10진수로는 (나)입니다.
정답
받는 답: (가) 1011 · (나) 183
B는 11이라 8 + 2 + 1로 1011입니다. 10진수는 11 × 16 + 7 = 183이고, 2진수 10110111을 자리 값으로 더해도 128 + 32 + 16 + 4 + 2 + 1 = 183입니다.
- 틀리는 꼴 (나)에 18: 두 자리 값 11과 7을 더하기만 했습니다. 왼쪽 자리는 16을 곱합니다.
- 틀리는 꼴 (나)에 117: B7을 11과 7을 이어 쓴 10진수로 읽었습니다. 16진수는 자리마다 16배입니다.
4. 다음 코드의 출력을 그대로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
#include <stdio.h>
int main(void) {
int a = 017;
int b = 0x1F;
printf("%d %d\n", a, b);
printf("%x %o\n", a + b, 8);
return 0;
}
정답
받는 답:
15 31
2e 10017은 0으로 시작하는 8진수라 8 + 7 = 15이고, 0x1F는 16 + 15 = 31입니다. 15 + 31 = 46은 16진수로 2e이고, 8은 8진수로 10입니다.
- 틀리는 꼴 첫 줄
17 31:017을 10진수로 읽었습니다. 앞의 0은 8진수 표시입니다. - 틀리는 꼴 둘째 줄
0x2e 10:%x가0x를 붙인다고 봤습니다. 숫자만 찍습니다.
5. 4바이트 칸 둘이 주소 0x1FC부터 이어 있습니다. 둘째 칸의 주소를 16진수로 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
정답
받는 답: 0x200
0x1FC에 4를 더합니다. C(12) + 4 = 16이라 그 자리는 0이 되고 1을 올리며, F(15) + 1 = 16이라 또 0이 되고 1을 올려 1 + 1 = 2가 됩니다.
- 틀리는 꼴 0x1F10: C + 4 = 16을 16진수 10으로 바꿔 그 자리에 그대로 적었습니다. 한 자리는 F(15)까지라 16이 되면 0을 남기고 윗자리로 1을 올립니다.
- 틀리는 꼴 0x1FD: 1바이트 다음 칸으로 셌습니다. 칸이 4바이트라 4를 더합니다.
처음이라면: C 언어 과목(준비 중)의 '2진수 · 16진수와 printf' 레슨에서 같은 규칙을 처음부터 천천히 봅니다.