본문 바로가기

CS/CS Book

[컴퓨터 시스템 딥다이브] ch04.01 ~ 03 바이너리와 데이터 표현

💡이 글은 컴퓨터 시스템 딥다이브(수잰 J. 매슈스, 한빛미디어 2024)를 읽고 적은 글입니다.

인트로부터 좋았다.

 

그냥 이건 이래가 아니라 어떻게 쓸모가 있고 인류가 정보를 기록하고 저장하는 과정의 발전과 사례들을 간략하게 나열해 주면서 거대한 저장 장치인 디지털 컴퓨팅을 소개해준다.

 

이 부분들을 통해 그냥 배우기만 했다면 컴퓨터의 쓸모에 대해 좀 더 생각해 보게 되지는 않았을 텐데 인류에게 있어 기록과 저장으로서의 컴퓨터의 의미를 떠올려볼 수 있어서 좋다.

 

바이너리와 데이터 표현은 컴퓨터 과학이 추상화의 예술이라는 말을 다시 한번 느끼게 해 준다.

전기신호가 있고(1), 없는(0) 것으로 이진수로 표현할 수 있고 그 이진수의 수열로 값을 표현할 수 있게 된다.

 

이진수 자릿수가 한 자리씩 늘어날 때마다 2배씩 늘어나서 기하급수적으로 커질 수 있다.

 

01011010이라는 동일한 비트열(이진수 수열)이 문자로 표현되면 ‘Z’이지만 그래픽 프로그램에서는 물고기의 꼬리지느러미를 의미하기도 한다.

같은 값이지만 어떤 맥락인지에 따라 해석이 달라진다.

이 부분이 특히 재밌다. 단순하게 0, 1로 밖에 표현 못하지만 그 수들의 나열을 어떻게 해석하는지에 따라 다양하게 해석할 수 있다. 표현할 수 있는 값도 자릿수를 늘림으로써 공간이 여유 있다면 무한히 늘릴 수 있다.

결국 모든 것을 표현할 수 있게 하는 방식이라는 생각이 들고 맥락이라는 점도 재밌다. ‘배’라는 단어가 있을 때 “밥 먹고 배를 두드렸다”라고 하면 신체의 일부인 배이겠지만 “출렁이는 바다 위에 있는 배”라고 하면 운송수단으로써의 배를 의미하게 된다. 사람의 언어와 닮아 있는 점도 재밌다.

4.1 숫자의 밑과 부호가 없는 정수

숫자의 밑은 주어진 숫자열을 어떻게 해석할지에 대한 부분이라 생각 든다.

1011 이 10진수라면 천십일이겠지만 2진수라면 10진수로 표현했을 때 11이 된다.

10진수, 2진수, 16진수 등등 각 진수와 그 값들의 자릿수에 대해 별다른 생각을 하지 않았었고 변환하는 것에만 신경 썼었는데 책에서는 각 자릿수에 있는 값이 전체 값에 기여한다는 관점으로 설명해 주어서 생각지 못한 관점이라 흥미로웠다.

7239라는 숫자가 있다고 한다면

와 같이 표현할 수 있다.

왼쪽에 있을수록 각 자릿수의 값이 전체 자릿수에 기여하는 부분이 커진다.

맨 왼쪽의 7 * 10^3은 7239에서 7000을 담당하므로 7000/7239만큼 기여한다.

2 * 10^2는 200/7239만큼 기여를 한다.

또한 표현을 일반화하여 보여주기에 10진수가 아닌 2진수, 16진수 일 때도 숫자의 밑을 바꾸어 이해하기 좋게 구성되어 있다.

16진수의 경우 2진수로 표현하기만 했다면 자릿수를 많이 차지하였을 것을 짧게 표현할 수 있게 해 준다. 16의 경우 2의 제곱수이므로 치환하기 더욱 편리하다.

컴퓨터에 값을 저장할 때는 미리 저장하기 전에 해당 타입의 변수를 저장할 공간의 크기를 정해야 한다. 그러므로 표현할 수 있는 값의 범위는 한정된다.

한정된 최댓값을 넘어서면 넘어선 값을 제대로 표현할 수 없다.

만약 세 자리까지 표현 가능한 변수가 있었다고 하였을 때 999 + 1을 하여 1000이 되는 경우 가장 큰 자릿수의 값이 1은 세 자리를 넘어서는 범위이므로 표현할 수 없다.

변수에 남아있는 비트 값은 000이 되게 된다.

이러한 상황을 오버플로우라고 한다(min값에서 더 작아지는 경우도 동일하게 오버플로우라고 한다.)

 

https://diveintosystems.org/book/C4-Binary/bases.html

 

그림 (a)의 경우 표현할 수 있는 범위가 무한한 경우이지만 그림 (b) 경우 표현 값의 범위가 유한하여 그 값이 넘어서는 경우 overflow가 되는 것을 볼 수 있다.

4.2 진수 변환

각 진수들은 다른 진수로 변환할 수 있다.

8491 ⇒ 1011 0100 1001 0001

0 b1011010010010001

B를 밑으로 하는 어떤 숫자의 각 자리를 오른쪽에서 왼쪽으로 $d_0, d_1, d_2$, …라고 하면 이 숫자를 10진수로 변환하는 일반적인 공식은 아래와 같다.

 

10진수 9742를 16진수로 변환한다고 할 때 두 가지 접근 방식이 있다.

  1. 16진수 제곱수 중 현재 10진수로 표현된 값(9742)을 포함할 수 있는 가장 큰 값을 찾고 그 값으로 9742를 나누었을 때의 몫을 적는다.
    1. 9742는 4096에 포함됨
    2. 4096은 16의 3 제곱. 즉, 4096으로 9742를 나눈 몫은 16진수로 변환하였을 때 값에서 세 번째 자리가 된다.
      1. d_3 ⇒ 2
    3. 나머지 값으로는 다시 위 과정을 반복하여 나머지가 0이 될 때까지 반복한다.
    4. 10진수 9742를 16진수로 표현한 값은 0x260E가 된다.
  2. 변환하려는 진법의 크기로 주어진 값을 나누고 나머지와 몫을 이용하는 방식
    1. 16으로 9742를 나누고 나머지를 $d_1$자리에 둔다.
    2. 나눈 몫을 다시 16으로 나누고 그 나머지 값을 $d_2$에 둔다.
    3. 몫이 0이 될 때까지 반복한다.

4.3 부호가 있는 수

이전까지는 부호가 없는 수라서 크기를 위주로 신경 썼었다.

부호가 있는 수의 경우 최상위 비트가 1이면 음수, 0이면 비음수라고 정하였다.

부호 없는 수에서 1011은 11이지만 부호가 있는 수에서는 -3이다.(1000 → 음수, 011 → 3)

 

이 방식의 단점은 0이 두 개가 된다. 0000(0), 1000(-0)

직관적이지도 않고 이런 경우를 처리하기 위해 하드웨어에서 별도의 처리가 필요해진다.

2의 보수를 이용하여 이런 문제를 해결한다.

https://diveintosystems.org/book/C4-Binary/signed.html

 

2의 보수의 경우 전체를 반전시키고 1을 더한다.

사실 완전히 이해되지는 않았다 어떻게 하면 음수를 표현할 수 있는지 과정(반전시키고 더하기 1)만을 알고 있다.

하지만 책을 보며 2의 보수에서는 부호에 영향을 주는 최상위 비트가 부호뿐만 아니라 전체 값에도 기여하게 만들었다.

1111이 -1이라는 것도 처음에는 잘 이해되지 않았지만

1000 → -8로 두고서 0111 →7을 합으로 표현하니 조금이나마 더 2의 보수의 쓸모가 느껴졌다.

 

https://diveintosystems.org/book/C4-Binary/signed.html

 

참고자료

Dive Into Systems