[CS300 #081] 2진수·16진수와 정수 표현 — 비트 묶음을 숫자로 읽는 법
컴퓨터공학 300 주제 시리즈의 081번째 글이다. 전체 지도는 여기.
한 줄 요약
컴퓨터 안의 모든 값은 0 과 1 의 묶음이고, 같은 비트 묶음을 “몇 진법으로, 몇 바이트로, 어떤 순서로” 읽느냐가 정수 표현이다. 16진수는 그 비트를 사람이 읽기 좋게 4개씩 묶어 적는 표기일 뿐이다.
왜 필요한가
5부의 주제문은 “코드가 실제로 도는 기계를 알면 성능 문제의 절반이 보인다” 이다. 그 기계가 다루는 단위가 비트와 바이트다. 이걸 읽지 못하면 다음 장면에서 막힌다.
- 패킷 덤프나 hexdump 에 찍힌
ea b0 80이 무엇인지 모른다. - 파일 권한
0755, 색상#FF8800, 메모리 주소0x7ffd...가 왜 그런 모양인지 모른다. - 네트워크로 보낸 정수 1000 이 상대편에서 엉뚱한 값으로 읽히는 이유(바이트 순서)를 설명하지 못한다.
진법 변환 자체는 초등 산수다. 중요한 것은 “같은 비트를 다르게 읽을 수 있다” 는 감각이다.
핵심 개념
자리값 표기
b 진법에서 숫자열 d(k-1) … d1 d0 의 값은 각 자리 숫자에 b 의 거듭제곱을 곱해 더한 것이다.
값 = d(k-1)·b^(k-1) + ... + d1·b^1 + d0·b^0
2진수 11001010 을 계산하면 다음과 같다.
비트: 1 1 0 0 1 0 1 0
자리: 128 64 32 16 8 4 2 1
합: 128 + 64 + 8 + 2 = 202
왜 16진수인가
16 = 2^4 이다. 그래서 2진수 4자리가 16진수 1자리에 정확히 대응한다. 변환에 곱셈이 필요 없고 표를 보며 끊어 읽으면 된다.
| 2진 | 16진 | 2진 | 16진 |
|---|---|---|---|
| 0000 | 0 | 1000 | 8 |
| 0001 | 1 | 1001 | 9 |
| 0010 | 2 | 1010 | A |
| 0011 | 3 | 1011 | B |
| 0100 | 4 | 1100 | C |
| 0101 | 5 | 1101 | D |
| 0110 | 6 | 1110 | E |
| 0111 | 7 | 1111 | F |
1100 1010 은 C A, 곧 0xCA 다. 한 바이트(8비트)는 항상 16진수 두 자리로 적힌다. hexdump 가 바이트마다 두 글자씩 찍는 이유다. 8진수는 3비트씩 묶는 표기이고, 유닉스 파일 권한(rwx 3비트 × 3)과 잘 맞아서 아직 쓰인다.
10진수를 b 진수로 바꾸기
b 로 나눈 나머지를 모으고 마지막에 뒤집는다.
2026 ÷ 16 = 126 나머지 10 (a)
126 ÷ 16 = 7 나머지 14 (e)
7 ÷ 16 = 0 나머지 7
→ 0x7ea
비트·바이트·워드
| 단위 | 크기 | 부호 없는 범위 |
|---|---|---|
| 비트 | 1 | 0~1 |
| 니블 | 4비트 | 0~15 |
| 바이트 | 8비트 | 0~255 |
| 16비트 | 2바이트 | 0~65,535 |
| 32비트 | 4바이트 | 0~4,294,967,295 |
| 64비트 | 8바이트 | 0~2^64−1 |
n 비트로는 2^n 개의 서로 다른 패턴을 만든다. 부호 없는 정수로 읽으면 0 부터 2^n − 1 까지다. 음수를 어떻게 넣는지는 다음 글(2의 보수)에서 다룬다. “워드” 는 CPU 가 한 번에 다루는 자연스러운 크기를 가리키는 말이라 아키텍처마다 뜻이 다르다는 점에 주의한다.
바이트 순서 (엔디언)
정수 1000 은 32비트로 0x000003E8 이다. 이 네 바이트를 메모리에 어떤 순서로 놓을지는 정해져 있지 않다.
주소 +0 +1 +2 +3
빅 엔디언 00 00 03 E8 (큰 자리부터)
리틀 엔디언 E8 03 00 00 (작은 자리부터)
x86-64 와 대부분의 ARM 리눅스 환경은 리틀 엔디언으로 동작한다. 반면 TCP/IP 헤더의 정수는 빅 엔디언으로 적는 것이 관례이고, 그래서 빅 엔디언을 “네트워크 바이트 순서” 라고 부른다. 같은 바이트를 서로 다른 순서로 해석하면 1000 이 3,892,510,720 (0xE8030000) 으로 읽힌다.
숫자가 아닌 것도 비트다
문자열도 결국 바이트열이다. UTF-8 에서 A 는 1바이트 0x41, 가 는 3바이트 EA B0 80 이다. 이 바이트를 텍스트로 옮겨 적을 때 쓰는 대표 방식이 16진수(Base16)와 Base64 이며, RFC 4648 에 정의돼 있다.
직접 해 보기
python3 로 실행해 확인한 코드다.
n = 202
print(bin(n), oct(n), hex(n))
print(int("11001010", 2), int("ca", 16), 0xCA, 0b1100_1010)
print(f"{n:08b} {n:02X} {n:#x}")
def to_base(n, b):
digits = "0123456789abcdef"
if n == 0:
return "0"
out = []
while n > 0:
n, r = divmod(n, b)
out.append(digits[r])
return "".join(reversed(out))
for b in (2, 8, 16):
print(b, to_base(2026, b))
data = "가A".encode("utf-8")
print(data, data.hex(" "), len(data))
print((1000).to_bytes(4, "big").hex(" "), (1000).to_bytes(4, "little").hex(" "))
print(int.from_bytes(b"\x00\x00\x03\xe8", "big"))
print((255).bit_length(), (256).bit_length())
출력:
0b11001010 0o312 0xca
202 202 202 202
11001010 CA 0xca
2 11111101010
8 3752
16 7ea
b'\xea\xb0\x80A' ea b0 80 41 4
00 00 03 e8 e8 03 00 00
1000
8 9
to_bytes 의 두 번째 인자만 바꿔도 같은 1000 이 다른 바이트열이 된다. bit_length() 는 그 수를 표현하는 데 필요한 최소 비트 수다. 255 는 8비트, 256 은 9비트가 필요하다.
현업에서는
- 덤프 읽기:
xxd,hexdump -C,tcpdump -X출력은 전부 바이트당 16진수 두 자리다. UTF-8 한글이EA~ED로 시작하는 3바이트 묶음으로 보이면 인코딩 문제를 바로 의심할 수 있다. - 비트 플래그: 리눅스 파일 권한
0644, 네트워크 서브넷 마스크/24=0xFFFFFF00, 기능 플래그를 정수 하나에 담는 설계 모두 비트 단위 읽기를 전제로 한다. - 직렬화: 바이너리 프로토콜을 직접 짤 때 엔디언을 명시하지 않으면 서로 다른 언어·기계 사이에서 값이 깨진다. 파이썬
struct, 자바ByteBuffer.order()처럼 순서를 지정하는 API 를 쓴다. - 쿠버네티스 매니페스트: 볼륨의
defaultMode같은 권한 값은 같은0644를 YAML 에선 8진수로 적을 수 있지만 JSON 에는 8진수 리터럴이 없어 10진수420으로 적어야 한다. 홈랩에서 시크릿 마운트 권한이 기대와 다르게 나오면 이 진법 차이부터 의심하는 것이 빠르다.
확인 문제
- 2진수
1011 0110을 10진수와 16진수로 바꿔라. - 16비트 부호 없는 정수의 최댓값을 10진수와 16진수로 적어라.
- 32비트 정수
0x12345678을 리틀 엔디언으로 메모리에 놓으면 주소 +0 에 무엇이 오는가. - 왜 8진수는 3비트, 16진수는 4비트 단위로 끊어 읽을 수 있는가.
풀이
- 128+32+16+4+2 = 182,
0xB6. - 2^16 − 1 = 65,535 =
0xFFFF. - 가장 낮은 자리 바이트
0x78. 순서는78 56 34 12. - 8 = 2^3, 16 = 2^4 이라 자릿수 하나가 정확히 3비트·4비트에 대응하기 때문이다.
더 읽을거리 (References)
- Python 공식 문서, Built-in Types — int 메서드(to_bytes, from_bytes, bit_length)
- Python 공식 문서, Built-in Functions — bin, hex, oct, int
- RFC 4648 — The Base16, Base32, and Base64 Data Encodings
- Randal E. Bryant, David R. O’Hallaron, Computer Systems: A Programmer’s Perspective, 3rd ed., Pearson. 2장.