진법에 대하여 - 2, 16진법과 기타 등등...

오세영·2026년 1월 29일

[잡다함]

목록 보기
6/6

dreamhack에서 해킹 공부를 시작했다. 그런데 여기서도 강조하는 바로 진법.
하지만 나에게 16진수는 너무나도 생소했는데... 그렇기에 그냥 진법들에 대한 개념을 확실히 잡고 가보기로 했다. 우선 2진법부터.


2진법

2진법: 두 종류의 숫자만을 이용하여 수를 나타내는 수 체계

2진수: 관습적으로 0과 1의 기호를 쓰며 이들로 이루어진 수

- 수 연산

(우리가 주로 사용하는 것은 10진법이다.)

10진수에서 1은 2진수에서도 마찬가지로 1이지만,
10진수에서 3은 2진수로 11로 나타낸다.
10진수에서 35는 2진수로 100011로 나타낸다.

읽는 법은 간단하다. 오른쪽에서부터 읽으며 자릿수에 따라 제곱을 1씩 키운다고 생각하면 된다. 가장 오른쪽은 2의 0제곱, 그의 왼쪽은 2의 1제곱, 그리고 2의 2제곱... 그렇게 점점 커진다.

(1byte는 8bit이기에 연습용으로는 8bit를 사용하는 것이 좋다. 즉, 00000000.)

- 논리 연산

일반적으로 1은 참(True), 0은 거짓(False)로 사용된다.
(물론, c언어에서는 0은 False, 나머지 수는 전부 True로 작용한다.)

- 보수

보수(補數): 어떤 기준값이 되도록 부족한 값을 채워주는 수

예시)

  • 1에 대한 10의 보수: 10 - 1 = 9
  • 3에 10의 보수: 10 - 3 = 7
  • 3에 16의 보수: 16 - 3 = 13

- 1의 보수

1의 보수: 모든 비트를 반전(0↔1) 한 것
(8bit일 때 범위: -127 ~ +127, 0이 두 개 존재)

맨 왼쪽 bit가 1일 때 음수이지만, 예외적으로 모든 bit가 1일 때에는 -0이다.
(아래의 표와 같은 이유로 0이 2개이다.)

1의 보수를 적용한 2진수

2진수 10진수
00000000 0
11111111 -0
00000101 5
11111010 -5

11111010은 1로 설정된 비트 위치를 기준으로 계산하면 2 + 8 + 16 + 32 + 64 = 122이지만, 1의 보수 방식이 적용되었기에 이것을 다시 0과 1을 뒤집어 읽어야 한다. 그럼 00000101이 되고 5이지만, 1의 보수 방식이었으니 - 부호를 붙인다. = -5

(이 방식은 그저 이론 이해용이지 거의 사용하지 않는다.)

- 2의 보수

2의 보수: 1의 보수 + 1
(8bit일 때 signed 범위: -128 ~ 127)

(단순히 1의 보수의 문제점을 개선했다고 생각하면 된다.)

11111111이 -0이 되지 않는다. 1을 더할 경우, 00000000이 되기 때문이다.

12 + 19를 했을 때, 2와 9를 더하면 11이 되어 10의 자리가 올라가듯이 이진수에서는 0과 1만 존재하기에 1을 더하면 차례대로 올림되어 전부 사라져 버린다.

8bit 기준 연산에서는 최상위 비트에서 발생한 올림(carry)은 저장되지 않는다. 이로 인해 signed 정수에서는 오버플로우가 발생할 수 있다.

2의 보수를 적용한 2진수

2진수 10진수
00000000 0
00000101 5
11111011 -5

00000101 + 11111011을 할 경우, 위에서 설명한 올림의 방식으로 1이 전부 사라지게 되어 0이 된다.

물론 signed 정수의 2의 보수 방식에서는 맨 왼쪽 bit를 음수 판별에 사용한다.

signed는 음수를 포함했다는 것을 의미하며, unsigned는 오로지 양수만 존재함을 의미한다. c언어에서 int형과 같이 숫자 자료형을 선언할 때는 기본적으로 signed로 설정된다. 그렇기에 만약 양수만을 허용할 것이라면 unsigned int a;라고 선언하면 된다.

- 오버플로우

오버플로우(overflow): 표현할 수 있는 범위를 넘어간 상태

만약 2진수로 표현된 64와 64를 더한다면 어떻게 될까?
이는 01000000(64) + 01000000(64)가 되고, 이를 더하면 10000000(signed로 해석하면 -128)이 되어 버린다.

또한 예를 들어 53 + 96는 값으로 149가 나와야 하지만 이를 컴퓨터가 연산하면 00110101(53) + 01100000(96) = 10010101. 이것은 signed 정수 규칙에 따라 음수로 해석되므로, 2의 보수 해석에 의해 01101011(107)으로 읽히게 된다. 즉, -107로 값이 깨지는 것을 볼 수 있다.

다음과 같이 올림이 잘못되어 부호가 깨지는 것을 오버플로우라고 한다.

c언어로 해보았을 때


16진법

16진법(Hexadecimal): 십육을 밑으로 하는 기수법

  • unsigned 8bit일 때 범위: 0 ~ 255
  • signed 8bit일 때 범위: −128 ~ 127

숫자 10개(0~9)와 더불어 알파벳 A~F가 사용된다.

프로그래밍 언어에서는 16진수를 표현할 때 앞에 0x를 붙여서 사용한다.
단순히 0x를 생략하고 작성하면, 컴파일러(또는 인터프리터)는 이를 기본적으로 10진수로 해석한다.

10진수 16진수 16진수 (0x 표기)
1 1 0x1
... ... ...
9 9 0x9
10 A 0xA
11 B 0xB
12 C 0xC
13 D 0xD
14 E 0xE
15 F 0xF
16 10 0x10

10진수 ↔ 16진수 예시 (unsigned 8bit 기준)

10진수 16진수 (0x 표기)
16 0x10
25 0x19
36 0x24
60 0x3C
255 0xFF

- 10진수 → 16진수

10진수를 16진수로 변환하는 것은 꽤 간단하다.
단순히 10진수인 수를 16으로 나누고, 몫은 앞자리에 나머지는 뒷자리에 할당하면 된다.

60을 16으로 나누면 몫은 3이고, 나머지는 12가 된다. 12에 대응하는 16진수는 C이다. 그렇기에 0x3C가 된다.

- 16진수 → 10진수

8bit를 기준으로 할 때, 0x45에서 앞자리인 4에 16을 곱하고, 뒷자리를 더하면 간단하게 10진수로 변환할 수 있다. 즉, 0x45는 69.

- signed일 때

2진수 때처럼 복잡하게 계산할 필요 없다. signed에서는 음수의 범위가 미리 정해져 있다. 이 범위는 비트 수와 signed/unsigned 여부에 의해 결정된다. 8bit를 기준으로 0x00 ~ 0x7F는 양수, 0x80 ~ 0xFF는 음수로 표현한다.

음수 범위에 포함된 수는 컴퓨터가 값 - 256을 하여 음수로 바꿔 계산한다.

정리 (8bit 기준)

  • 양수 범위 : 0x00 ~ 0x7F : 그대로 양수
  • 음수 범위 : 0x80 ~ 0xFF : 값 - 256

-끝-

profile
안녕하세요? 시스템 소프트웨어 || 백엔드 개발자 지망생입니다:D

0개의 댓글