본문 바로가기
Converter

진법 변환기 (2 · 8 · 10 · 16진)

한 값을 2·8·10·16진으로 동시에, 음수는 2의 보수로

10진 (decimal)로 해석합니다. 접두어(0x·0b·0o)·구분자(_)·음수(-)를 받습니다

2진 (binary)0b11111111
8진 (octal)0o377
10진 (decimal)255
16진 (hex)0xFF

고정 폭 비트 (2의 보수)

부호 있는 정수 기준
8비트부호 있는 8비트 범위(−27 ~ 27−1)를 벗어남
16비트0x00FF0000 0000 1111 1111
32비트0x000000FF
64비트0x00000000000000FF

2진은 8·16비트만 표시합니다(32·64비트는 너무 길어 16진만 보입니다). 음수를 넣으면 왜 −1이 0xFF가 되는지 각 폭에서 확인할 수 있습니다.

진법은 “같은 수를 다르게 적는 법”일 뿐입니다

먼저 오해 하나를 풉니다. 2550xFF0b11111111 다른 수가 아닙니다. 완전히 같은 하나의 수를 세 가지 방식으로 적은 것뿐입니다. 진법을 바꾼다고 값이 변하지 않습니다 — 표기만 바뀝니다. 위 변환기가 한 값을 네 칸에 동시에 보여 주는 것도 이 점을 눈으로 확인하시라는 뜻입니다.

그렇다면 왜 굳이 여러 진법이 필요할까요. 컴퓨터는 전기가 켜지고 꺼지는 두 상태, 즉 2진수로만 동작합니다. 사람은 손가락이 열 개라 10진수가 편합니다. 문제는 2진수가 너무 길다는 것입니다 — 한 바이트가 11111111처럼 여덟 자리라, 몇 바이트만 늘어놓아도 읽을 수가 없습니다. 그래서 2진수를 사람이 읽기 좋게 묶는 중간 진법이 필요했고, 그게 16진수와 8진수입니다.

16진수: 왜 하필 4비트씩 묶나

핵심은 16 = 24 이라는 데 있습니다. 16진수 한 글자가 정확히 4비트(니블)를 나타냅니다. 그래서 8비트 바이트 하나가 16진수 두 글자로 딱 떨어집니다.

11111111   (2진, 8비트)
1111 1111   4비트씩 끊으면
   F    F   각 묶음을 16진 한 글자로
= FF        바이트 하나 = 16진 두 글자

이 “딱 떨어짐” 덕분에 16진수는 비트를 다루는 거의 모든 곳에 들어갔습니다.

  • 색상 #6366F1 — R·G·B 각 한 바이트(0~255)를 16진 두 글자씩. 같은 값을 색상 변환기가 RGB로 풀어 줍니다.
  • 메모리 주소 0x7FFE... — 주소는 비트 폭이 정해져 있어 16진이 자연스럽습니다.
  • 유니코드 U+D55C(‘한’) — 코드포인트도 16진으로 적습니다.

16진수를 “어려운 것”이 아니라 “2진수를 네 칸씩 접은 것”으로 보면, 0xFF가 왜 “전부 1인 한 바이트”인지가 바로 읽힙니다.

8진수와 chmod 755

8진수는 8 = 23, 즉 3비트씩 묶습니다. 요즘은 16진수에 밀려 자주 안 보이지만, 딱 한 곳에서 지금도 매일 쓰입니다 — 유닉스 파일 권한입니다. 권한은 읽기·쓰기·실행(rwx) 세 비트가 한 묶음인데, 3비트가 8진수 한 자리에 정확히 맞아떨어집니다.

7 = 111 = rwx   (읽기·쓰기·실행 전부)
5 = 101 = r-x   (읽기·실행)
4 = 100 = r--   (읽기만)

755 → rwx r-x r-x
      소유자 그룹 기타

chmod 755가 외계어처럼 보였다면, 그건 8진수라는 걸 몰랐기 때문입니다. 위 변환기에 755를 8진으로 넣어 2진 111101101을 보고 세 자리씩 끊으면, 권한 세 벌이 그대로 드러납니다.

음수는 어떻게 저장하나: 2의 보수

여기서부터가 대부분의 진법 변환기가 다루지 않는, 그러나 실무에서 가장 자주 헷갈리는 부분입니다. 컴퓨터에는 ‘−’ 기호를 담을 칸이 따로 없습니다. 0과 1밖에 없으니까요. 그러면 음수를 어떻게 표현할까요.

가장 단순한 생각은 “맨 앞 비트를 부호로 쓰자”입니다. 하지만 그러면 +0−0이 따로 생기고, 덧셈 회로와 뺄셈 회로를 따로 만들어야 합니다. 그래서 실제로 쓰는 방법은 2의 보수입니다. 음수를 만드는 규칙은 “비트를 전부 뒤집고 1을 더한다” 입니다.

 1 = 00000001
뒤집기 → 11111110
+1     → 11111111  = -1  (8비트)

그래서 −1은 8비트에서 0xFF, 16비트에서 0xFFFF, 32비트에서 0xFFFFFFFF입니다. 위 변환기의 아래쪽 “2의 보수” 칸에 음수를 넣으면 이 패턴이 폭마다 어떻게 채워지는지 보입니다. 이 방식의 진짜 장점은 뺄셈이 덧셈이 된다는 것입니다 — 5 − 35 + (−3)로, 같은 덧셈 회로 하나로 처리할 수 있습니다.

−128~127이라는 비대칭

2의 보수에는 유명한 함정이 하나 있습니다. 8비트로는 256가지를 표현하는데, 그중 하나는 반드시 0이어야 합니다. 0을 양수 쪽에 넣으면 양수는 0~127(128가지), 음수는 −1~−128(128가지)로 갈립니다. 결과적으로 음수 쪽이 하나 더 멀리 갑니다 — 최솟값 −128은 있는데 최댓값은 +127까지뿐입니다. 그래서 −128의 부호만 뒤집어 128을 만들려 하면 8비트 범위를 넘어 오버플로가 납니다. 이 도구가 각 폭의 범위를 −2n−1 ~ 2n−1−1로 적고, 범위를 벗어난 값은 억지로 자르지 않고 “벗어남”이라고 알리는 이유입니다.

왜 BigInt인가: 253의 벽

자바스크립트의 보통 숫자(Number)는 64비트 부동소수점이라, 정수를 정확히 담을 수 있는 한계가 253 입니다. 그 위로 넘어가면 조용히 틀립니다.

9007199254740993        // 2^53 + 1, 우리가 원하는 값
Number("9007199254740993")
→ 9007199254740992       // 마지막 자리가 조용히 바뀐다

64비트 해시, 스노플레이크 ID, 큰 비트마스크를 16진으로 다루다 보면 이 범위를 예사로 넘습니다. 그래서 이 변환기는 Number가 아니라 BigInt로 계산합니다 — 자릿수 제한 없이 정확합니다. “큰 수를 넣었더니 값이 미묘하게 달라지더라”는 경험이 있다면, 십중팔구 이 253 벽에 부딪힌 것입니다. 참고로 용량 단위의 210 vs 103 이야기는 파일 크기 변환기에서 다룹니다 — 같은 “2의 거듭제곱” 감각이 거기서도 핵심입니다.

정리

진법은 같은 수의 다른 표기입니다. 16진수는 24 라 4비트씩, 8진수는 23 이라 3비트씩 묶어 2진수를 사람이 읽게 해 주고, 그래서 각각 색상·주소·유니코드와 파일 권한이라는 자리를 차지했습니다. 음수는 2의 보수로 저장되어 −10xFF가 되고, 그 표현에는 −128~127이라는 비대칭이 따라옵니다. 그리고 큰 수를 정확히 다루려면 Number 253 벽 너머까지 가는 BigInt가 필요합니다. 이 네 가지를 알면 화면의 16진 문자열이 더는 암호처럼 보이지 않습니다.

자주 묻는 질문

왜 16진수를 쓰나요? 그냥 10진수면 안 되나요?
16진수 한 글자가 정확히 4비트(2의 4제곱=16)를 나타내기 때문입니다. 그래서 8비트 바이트 하나가 16진수 두 글자로 딱 떨어집니다(예: 11111111 → FF). 10진수는 이 정렬이 안 맞아서, 비트를 다루는 색상 코드(#RRGGBB), 메모리 주소, 유니코드 코드포인트(U+D55C)가 전부 16진수를 씁니다. 2진수는 너무 길고 10진수는 비트 경계가 안 보이는데, 16진수가 그 사이에서 '비트를 사람이 읽는' 다리 역할을 합니다.
chmod 755의 숫자는 무슨 뜻인가요?
8진수입니다. 유닉스 파일 권한은 읽기·쓰기·실행(rwx) 세 비트가 한 묶음이고, 3비트는 8진수 한 자리(2의 3제곱=8)에 정확히 들어맞습니다. 7은 2진수 111이라 rwx 전부, 5는 101이라 읽기+실행입니다. 그래서 755는 '소유자는 rwx, 그룹과 나머지는 r-x'라는 뜻입니다. 이 변환기에 755를 8진으로 넣으면 2진 111101101이 나오는데, 세 자리씩 끊어 읽으면 권한이 그대로 보입니다.
-1이 왜 0xFF나 0xFFFFFFFF로 나오나요?
2의 보수 표현이기 때문입니다. 컴퓨터는 음수를 위해 따로 '−' 기호를 저장하지 않고, 비트 전체를 뒤집는 방식으로 음수를 만듭니다. 8비트에서 -1은 00000001을 뒤집고 1을 더한 11111111 = 0xFF입니다. 폭이 커지면 앞이 전부 1로 채워져 16비트는 0xFFFF, 32비트는 0xFFFFFFFF가 됩니다. 이 방식의 장점은 뺄셈을 덧셈 회로로 처리할 수 있다는 것입니다.
2의 보수 범위가 왜 -128~127처럼 비대칭인가요?
0이 양수 쪽에 자리를 하나 차지하기 때문입니다. 8비트로 256가지를 표현하는데, 그중 하나는 0이어야 합니다. 0을 양수 쪽에 넣으면 양수는 0~127(128가지), 음수는 -1~-128(128가지)로 나뉩니다. 그래서 양수 최댓값(127)이 음수 최솟값의 절댓값(128)보다 하나 작습니다. 이 때문에 -128을 부호만 바꿔 128을 만들려다 오버플로가 나는 버그가 실제로 있습니다.
아주 큰 수도 정확하게 변환되나요?
네. 이 도구는 자바스크립트의 BigInt로 계산해서 자릿수 제한이 사실상 없습니다. 보통의 자바스크립트 숫자(Number)는 2의 53제곱부터 정수를 정확히 담지 못해서, 9007199254740993 같은 값이 조용히 ...992로 바뀝니다. 64비트 해시나 큰 ID를 다룰 때 이 오차가 실제 버그가 되는데, BigInt는 그 범위를 넘어서도 정확합니다.
0x, 0b, 0o는 무슨 뜻인가요?
각각 16진(0x)·2진(0b)·8진(0o)이라는 표시입니다. 같은 숫자 11이 진법에 따라 전혀 다른 값이라(2진 11은 3, 16진 11은 17), 코드에서는 접두어로 진법을 못박습니다. 이 변환기는 접두어가 붙어 있으면 알아서 벗기고 읽으므로, 0xFF를 그대로 붙여넣어도 됩니다.