컴퓨터 내부의 언어체계와 표현방법

문자언어란?

사람들은 언어를 사용해서 소통한다. 같은 문맥을 공유하는 사람들끼리는 언어로 제대로 된 의사소통을 할 수 있다. 반대의 예를 들어보면, Camisole은 미국에서 여성용 속옷을 말하지만 프랑스에서는 구속복을 뜻한다. 우리나라의 욕설은 어느 나라에서는 칭찬의 언어일 수도 있다.

문자 언어는 기호를 나열한 것이다. 예를 들어 격 은 ‘ㄱ’ ‘ㅕ’ ‘ㄱ’ 이 합쳐진 것이고, Close는 ‘C’ ‘l’ ‘o’ ‘s’ ‘e’가 합쳐진 문자이다. 읽는 방식도 다를 수 있다. 왼쪽에서 오른쪽으로 읽는 것, 오른쪽에서 왼쪽으로 읽는 것, 위에서 아래로 읽는 것 읽는 것의 방식들 또한 여러가지이다.

‘격’ 이나 ‘Close’처럼 ‘ ’를 상자로 표현하자면, 기호가 들어갈 상자와 그 상자에 들어갈 기호, 상자의 순서 이 세 가지가 문자 언어의 틀을 이룬다고 볼 수 있다.

  • 기호가 들어갈 상자
  • 그 상자에 들어갈 기호
  • 상자의 순서

논리연산

컴퓨터에서는 이 상자를 ‘비트(Bit)’라고 표현한다. 2진법을 사용한다는 뜻의 바이너리(Binary)와 디지트(Digit)가 합쳐진 단어이다. 예(True)와 아니요(False)를 담을 수 있으며 날씨가 추운가? 내 모자의 색은 파란색인가? 등의 질문에 하나의 비트로 표현할 수 있고(예, 아니요) 그곳의 장소는 어디입니까?의 질문에는 하나의 비트로 표현할 수 없다. 자연어에서는 여러 예/아니요 구절을 엮어서 한 문장으로 만들 수 있다. ‘모자는 파란색이고 상의가 파란색이라면 하의를 검정색으로 입어라’, ‘비가 오고 학교에 가지 않는다면 나가지말아라’ 등등, 다른 비트들이 표현하는 내용으로부터 새로운 비트를 만들어내는 이런 동작을 논리 연산(Logic operation) 이라고 한다.

불리언 대수(Boolean algebra)

대수는 ‘수에 대한 연산 규칙의 집합’인 것처럼, 불리언 대수도 ‘비트에 대해 사용할 수 있는 연산 규칙의 집합’ 이다. 일반 대수와 마찬가지로 결합 법칙, 교환 법칙, 분배 법칙을 불리언 대수에 적용할 수 있다.

기본적인 불리언 연산자는 NOT, OR, AND 세 가지 이며 합성연산 XOR 연산자가 있다.

논리연산자     결과
NOT   거짓
  거짓  
AND
  거짓 거짓
  거짓 거짓 거짓
OR
  거짓
  거짓 거짓 거짓
XOR 거짓
  거짓
  거짓 거짓 거짓

● NOT: ‘반대’ 를 의미한다. 예를 들어 거짓인 비트에 NOT을 하면 참이 되고 참인 비트에 NOT을하면 거짓이 된다.

● AND: ‘동시에 참’ 을 의미한다. 2비트 연산의 경우에 첫 번째 비트가 참이고 두 번째 비트가 참이라면 참을, 첫 번째 비트가 참이고 두 번째 비트가 거짓이라면 거짓을, 첫 번째 비트가 거짓이고 두 번째 비트가 거짓이면 거짓을 반환한다.

● OR: ‘~중 하나가 참’ 을 의미한다. 2비트 연산의 경우에 첫 번째 비트가 참이고 두 번째 비트가 참이라면 참을, 첫 번째 비트가 참이고 두 번째 비트가 거짓이라면 참을, 첫 번째 비트가 거짓이고 두 번째 비트가 거짓이면 거짓을 반환한다.

● XOR: ‘다를 경우’ 를 의미한다. 배타적(Exclusive) OR이라고 하는데, 줄여서 XOR이라고 한다. 첫 번째 비트와 두 번째 비트가 다른 값일 경우에만 참이 된다. 두 값이 모두 참이나 거짓이라면 거짓을, 두 값중 어느 하나가 참이면 참을 반환하는 방식이다.

드모르간의 법칙

오거스터스 드모르간(Augustus De Morgan)은 불리언 대수에 적용할 수 있는 법칙을 추가로 알아냈다. 이 법칙은 드모르간의 이름을 따서 드모르간의 법칙 이라고 불리고 ‘a AND b’라는 연산은 ‘NOT(NOT a OR NOT b)’와 같다 고 한다. a AND b == NOT(NOT a OR NOT b)
그냥 ‘a AND b’ 를 사용할 수도 있겠지만, 프로그래밍을 하다보면 입력을 항상 원하는 형태로 얻을 수는 없기 때문에 이런 법칙이 유용하게 쓰일 일이 많다고한다.

긍정적인 논리(Positive logic) 에 더해 부정적인 논리(Negative Logic) 을 기술하는 명제를 사용할 때 드모르간의 법칙을 활용할 수 있다.

어둡다 공부를 해야한다 스탠드를 킨다 l NOT 어둡다 NOT 공부를 해야한다 NOT 스탠드를 킨다
F F F l F F F
T F T l T F F
F T T l F T F
T T T l T T T

이 표에서 왼쪽(긍정)은 OR 연산으로, 오른쪽(부정)은 드모르간의 법칙을 활용한 AND 연산을 사용했다. 드모르간의 법칙이 없었다면 ‘NOT NOT 어둡다 OR NOT NOT 공부를 해야한다’로 오른쪽(부정)을 표현해야했던 것이다. 물론 이런 논리도 제대로 작동은 하지만 하드웨어의 비용을 최적화하고(연산자의 개수가 줄기 때문에) 계산을 효율적으로 하기 위해 드모르간의 법칙을 사용하는 것이 좋다고 한다.

정수를 표현하는 방법

10진수

우리들은 보통 10진수를 사용한다(손가락이 10개이기 때문에). 아까처럼 상자로 표현하자면 10진수에서의 상자 안에는 10가지의 기호인 숫자를 담을 수 있다. 이 때 순서는 오른쪽에서 왼쪽으로 차례차례 담겨가며 각 상자마자 일의 자리, 십의 자리, 백의 자리 등으로 불린다. 각 이름은 10의 거듭제곱에 해당한다. 즉 $10^0 = 1$, $10^1 = 10$, $10^2 = 100$이다. 이 10진수는 밑base을 10으로 하기 때문에 밑이 10인 시스템이라고 한다. 수의 값은 상자 안에 든 내용물의 값과 상자의 값을 곱한 것을 모두 더한 걸로 결정된다. 예를 들어 3022라는 숫자는 \(3*10^3 + 0*10^2 + 2*10^1 + 2*10^0\) 을 모두 합한 값이다.

2진수

비트를 사용해 값을 만들 때도 비슷하다. 2진수니까 10진수와는 다르게 1과 0, 두 가지의 숫자 만으로 표현하는 것인데 어렵게 생각하지말고 10진수의 개념과 같지만 10이 2로 바뀐것 뿐이다라고 생각하면 쉽다. 예를 들어 10진수에서는 9의 뒤부터(10~)는 1의 자리 상자로는 표현할 수 없어 상자가 하나 더 늘어나는데, 2진수의 경우는 1의 뒤(2~)를 상자 하나로 표현할 수 없으므로 상자 하나가 늘어나는 방식이라고 생각하면 된다. 2진수는 기호가 2개이므로 상자도 2의 거듭제곱만큼 늘어난다.

3022는 2진수로 101111001110인데 $1 * 2^{11} + 0 * 2^{10} + 1 * 2^9 + 1 * 2^8 + 1 * 2^7 + 1 * 2^6 + 0 * 2^5 + 0 * 2^4 + 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 0 * 2^0$ 이고 1과 곱한 2의 지수들의 표를 보면

2진수 10진수
$2^{11}$ 2048
$2^9$ 512
$2^8$ 256
$2^7$ 128
$2^6$ 64
$2^3$ 8
$2^2$ 4
$2^1$ 2
합계 3022

로 맞는 모습을 볼 수 있다.

10진수로는 3022는 네 자리 숫자라고 말할 수 있고, 2진수로는 12비트 수($2^0 $을 포함하므로)가 되는 것이다. 이렇듯 상자의 수가 늘어날수록 표현할 수 있는 값의 범위가 늘어나는데, 흔히 말하는 컴퓨터 사양의 32비트와 64비트의 차이가 크다는 것도 이를 보면 체감이 된다.

비트 개수 값의 개수 값의 범위
4 16 0 ~ 15
8 256 0 ~ 255
12 4096 0 ~ 4095
16 65536 0 ~ 65535
20 1048576 0 ~ 1048575
24 16777216 0 ~ 16777215
32 4294967296 0 ~ 4294967295
64 18446744073709551616 0 ~ 18446744073709551615

4비트로 16가지의 수를 표현할 수 있지만 그 범위가 굳이 0~15인 것은 아니다. 해석에 따라(문맥에 따라) 새로운 범위를 만들 수도 있는 것이다.

MSB와 LSB

2진수에서 가장 오른쪽 비트는 LSB(Least Significant Bit)라고 부르고 가장 왼쪽 비트는 MSB(Most Significant Bit)라고 부른다. 가장 오른쪽의 비트를 변경하면 가장 작게, 가장 왼쪽의 비트를 변경하면 가장 크게 변경되기 때문에 이런 이름이 붙었다고 한다.

2진수의 덧셈

2진수의 덧셈은 각 수의 비트끼리 XOR연산을 한 값과 같고 올림은 두 비트를 AND연산을 한 값과 같다.

1
2
3
1 = 001
5 = 101
6 . 110

오버플로(Overflow), 언더플로(Underflow)

사용할 비트의 개수로 표현할 수 있는 범위를 벗어나면 오버플로(Overflow)가 발생한다. 오버플로란 말은 MSB에서 올림이 발생했다는 의미이고 컴퓨터의 레지스터에는 이 오류(오버플로)를 담는 오버플로 비트(Overflow Bit)가 담겨있다.

MSB 위쪽에서 1을 빌려오는 것은 언더플로(Underflow) 라고 한다. 언더플로(Underflow)에 해당하는 코드 또한 컴퓨터에 담겨 있다.

비트의 음수 표현

부호와 크기 표현법

음수와 양수를 구분하기 위해서는 부호를 사용하는데, 비트의 경우에도 양부호와 음부호가 있고 이를 비트 하나를 써서 표현할 수 있다. MSB에 부호를 사용하기로 멋대로 결정했고 4비트라면 1비트(부호 비트, MSB)를 제외하고 3비트의 0~7가지 수를 표현할 수 있다. MSB가 0이면 양수를, 1이면 음수로 취급한다고 하면 총 15가지 음수와 양수를 취급할 수 있다(양수의 0과 음수의 0은 하나의 0이기 때문에 15가지임에 주의).

이렇게 한 비트를 부호에 사용하고 나머지 비트를 0부터의 거리(절댓값)을 표현하기 위해 사용하는 방법을 부호와 크기(Sign and Magnitude) 표현법 이라고 한다. 그러나 이 방법은 0을 표현하는 방법이 2가지(양, 음)이기 때문에 비용이 낭비되고 XOR과 AND를 통한 덧셈 계산을 할 수 없어서 널리 쓰이지는 않고 있다.

1의 보수 표현법

또 다른 방법으로는 양수의 모든 비트를 뒤집는 방법이 있다. 이런 방법을 1의 보수(One’s Complement)표현법 이라고 부른다. 부호와 크기 표현법과 마찬가지로 부호비트와 나머지비트로 이루어지며, NOT 연산을 통해 보수를 얻는다.
이 표현법 또한 0이 2가지 방식으로 표현된다는 단점이 있고 덧셈 또한 쉬운편은 아니다. 1의 보수 표현법에서 덧셈을 하려면 MSB쪽에서 올림이 발생했을 때 LSB로 올림을 전달하면 된다. 이를 순환 올림(End-Around Carry)이라고 부른다. 이 방식 또한 순환 올림의 하드웨어를 추가시켜야해서 좋은 방법은 아니라고 볼 수 있다.

1
2
3
4
5
6
7
  0010|+2
+ 1110|-1
─────────
  0000| 0(MSB에 올림 발생)
+ 0001| 1(순환올림)
─────────
  0001|+1(결과)

2의 보수 표현법

하드웨어를 추가하지 않고, XOR과 AND 연산만 사용해야한다면 이 표현법이 가장 좋을 것이다. +1(0001)에 더했을 때 0(0000)이 나오는 비트 패턴을 찾고 이 패턴을 -1이라고 부르자. 4비트의 경우 0001에 더했을 때 0이 나오는 패턴은 1111이다. 어떤 수의 비트의 각 비트에 NOT연산을 취하고, 1(0001)을 추가하면 음수를 얻을 수 있는 것이다. 이 때 MSB에 올림이 발생하면 올림은 버려버린다. 2의 보수 표현에는 0의 중복 표현이 없다(0000의 모든 비트를 뒤집으면 1111, 1을 더하면 [1]0000이 되는데 올림은 버리기 때문에 0000이 된다).

실수를 표현하는 방법

위의 것들은 전부 정수를 표현하는 방법이고, 실수를 표현하는 방법또한 다르다. 밑이 10(10진수)인 실수에는 10진 소수점decimal point이 포함된다. 따라서 밑이 2라면 실수를 표기하기 위한 2진 소수점 표현 방법이 필요하다(정수와 마찬가지로 문맥에 따라 실수를 표현하는 방법이 달라질 수 있음).

고정소수점 표현법, Fixed-Point

2진 소수법의 위치를 임의로 정하는 방법이다. 예를 들어 4비트 중 2비트 왼쪽은 정수부분, 오른쪽은 분수부분으로 사용하는 것이다. 이럴 경우 2비트가 소수 부분에 할당 되기 때문에 00($\frac{0}{4}$), 01($\frac{1}{4}$), 10($\frac{2}{4}$), 11($\frac{3}{4}$)의 4가지 분숫값을 표현할 수 있다.

정수 부분   분수 부분
0 0 . 0 0 0
0 0 . 0 1 $\frac{1}{4}$
0 0 . 1 0 $\frac{2}{4}$
0 0 . 1 1 $\frac{3}{4}$
0 1 . 0 0 1
0 1 . 0 1 1$\frac{1}{4}$
0 1 . 1 0 1$\frac{2}{4}$
0 1 . 1 1 1$\frac{3}{4}$
1 0 . 0 0 2
1 0 . 0 1 2$\frac{1}{4}$
1 0 . 1 0 2$\frac{2}{4}$
1 0 . 1 1 2$\frac{3}{4}$

10진수와의 차이점 : 10진수는 10의 거듭제곱을 분모로 사용하는 소수, 2진수는 2의 거듭제곱을 분모로 사용하는 소수

이런 접근 방법은 작동은 잘 하지만 필요한 비트의 개수가 많아 범용 컴퓨터에서는 잘 사용하지 않는다(디지털 신호 장치 등의 장치에는 쓰인다고 함). 예를 들어 보면 물리학에서 양자 역학의 기본 상수 중 하나인 플랑크 상수Planck’s constant(약 $6.63×10^{34}$와 아보가드로 수Avogadro’s constant(약 $6.02×10^{23}$)의 사이에는 약 $10^{57}$만큼의 범위가 있으며 이는 약 $2^{191}$에 달한다. 비트가 200개 정도 소모되는 셈이다.

부동소수점 표현법, Floating-Point

위의 설명만큼의 범위 등을 2진수로 표현하기 위해서 과학적 표기법scientific notation을 2진수에 적용한다. 10진 소수점 왼쪽이 한 자리 뿐인 소수, 즉 가수mantissa에 10을 몇 번(지수exponent) 거듭제곱한 값을 곱하는 방식으로 소수를 표현한다. 0.0011 대신 $0.11 * 10^{-3} $으로 표현하는 것이다($0.11 * 0.001 = 0.0011$ 으로 값이 같다). 주의할 점은 단순히 가수만 보았을 때 부동(不動, immobility)으로 오해할 수 있으나 부동(浮動, Floating)이다. 소수점이 고정되있지않고 둥둥 떠다닌다는 뜻이다. $1.1 * 10^{-1}$ 은 소수점 왼쪽 숫자의 자리가 $\frac{1}{10}$이고, $1.1 * 10^5$는 100000이다는 점에서 소수점의 위치는 정해져있지 않다는 것이다.

그러나 고정소수점 표현법과 마찬가지로 단점이 존재한다.

  1. 비트 조합 중 낭비되는 부분이 많음 >4비트에 4의 경우 $\frac{1}{2} * 2^3$와 $1*2^2$이 있음.
  2. 비트 패턴이 가능한 모든 수를 표현할 수 없음 >지수가 커질수록 가수의 한 패턴과 다른 패턴 사이의 값 차이가 커지기 때문

IEEE 부동소수점 수 표준

단점이 존재함에도 컴퓨터에서 계산을 수행할 때 실수를 표현하는 방법은 부동소수점 수 표준 이다. 가수와 지수에 각각 부호 비트를 사용하고 단점에서 사용한 예제(4비트)보다 더 많은 비트를 사용하지만 여러 가지 트릭으로 낭비되는 비트 조합을 최소화하고 반올림을 쉽게 한다. IEEE 754라는 표준은 이 모든 기능을 정의한다. IEEE754에서는 두 가지 부동소수점 수가 자주 쓰인다. 기본 정밀도single precision 부동소수점 수와 2배 정밀도double precision 부동소수점 수이다. 기본 정밀도 수는 32비트, 2배 정밀도 수는 64비트를 표현해 32비트에 비해 더 많은 비트 비용을 지불한다는 단점으로 정밀도를 높였다.

여러가지 트릭

정규화, Nomalization

이 ‘트릭’ 중 한가지로 ‘정규화’라는 트릭이 있다. 정규화란 가수를 조정해서 맨 앞(왼 쪽)에 0이 없게 만드는 것이다 가수를 조정하려면 지수도 조정해야한다.

DEC

디지털 이큅먼트 사Digital Equipment Corporation에서 고안한 트릭 중 한가지로 가수의 맨 왼쪽 비트가 1이므로 (정규화로 0이 없게 하므로) 이를 생략함으로 가수에 1비트를 더 사용할 수 있다.

이 외에도 여러가지 트릭이 있다.

2진 코드화 10진수 시스템, BCD

2진 코드화 10진수BCD, binary-coded decimal 4비트를 사용해 10진 숫자를 하나 표현한다. 12는 2진수로 1100이지만 BCD는 0001 0010 이다. 0001은 10의 자리의 1을, 0010은 1의 자리의 2를 표현한다. 10진수를 사용하는 우리에게는 훨씬 익숙한 방식이다. 과거의 컴퓨터에서는 BCD를 사용했지만 현대의 컴퓨터들은 더 이상 BCD를 주류로 사용하지 않는다(가속도 센서나 디스플레이 등에서는 사용을 한다곤 한다). 이유인 즉슨 BCD는 2진수를 효율적으로 활용하지 못하기 때문이다. BCD는 일반적인 2진수에 비해 더 많은 비트를 사용한다(위의 예제를 보면 알 수 있음). 과거에 비해 비트비용이 저렴해지긴 했으나 효율적이지 못하다는 점 때문에 비주류로 바뀌었다.

2진수를 다루는 쉬운 방법들

8진 표현법

8진 이라는 말은 밑이 ‘8’이라는 뜻이다. 2진수 비트를 3개씩 그룹으로 묶는 아이디어로 $2^3$이므로 0~7가지의 값을 표현할 수 있다. 표로 보는게 좀 더 쉬울 것이다. 9898을 예로 들어보자면 9898 = 10011010101010 의 괴랄한 2진수가 나온다. 그렇다면 이걸 오른쪽에서부터 3개씩 나누어 표로 표현하면

10 011 010 101 010
2 3 2 5 2

이 값이 나오고 실제로 9898(10)을 8진수로 변환한 값은 23252(8)이 나오게 된다. 2진수와 다르게 눈이 편안해지는 느낌을 받는다.

16진 표현법

8진 표현법이 아직도 쓰이기는 하지만 현재에는 16진 표현법hexadecimal representation이 널리 쓰이고 있다. 그 이유인즉슨 요즘의 컴퓨터는 8비트의 배수를 사용하기 때문에 8의 배수는 4(16비트 한 자리의 비트 수)로는 균일하게 나뉘어지지만 3(8비트 한 자리의 비트 수)로는 나뉘어지지 않기 때문이라고 한다.

16진수는 0~9 까지는 숫자를, 10~15까지는 a, b, c, d, e, f알파벳을 이용해 숫자를 표현한다고 믿기로 했다.

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 1001 d
0110 6 1010 e
0111 7 1000 f

이 표를 자연스레 머릿속에 떠올릴때까지 외워보는것도 좋을 것 같다.

8진 표현법에서 보였던 예시(9898)로 다시 표현해보자면 26aa가 나온다. 9898의 2진수인 10011010101010을 오른쪽부터 4개씩 나누어 9이상의 수는 알파벳으로 표현한 것이다. |10|0110|1010|1010| |—|—|—|—|–| |2|6|a|a|

숫자의 표현법은 어떻게 알까

그렇다면 사람들은 숫자만 보고 8진 표기인지, 16진 표기인지, 10진 표기인지 알 수 있을까? 수학책에서는 ‘아래 첨자’를 이용해 108, 1016, 1010 등으로 표현할 수 있지만 컴퓨터에서는 아래 첨자를 사용하는 것이 여간 불편한 것이 아니다. 그래서 우리들은 표기법을 사용하기로 약속했다. 여러 프로그래밍 언어에서 사용하는 표기법은 다음을 따른다

  • 0으로 시작하는 숫자는 8진수다. ex) 017
  • 1 ~ 9 사이의 숫자로 시작하는 숫자는 10진수다. ex) 123
  • 0x 가 앞에 붙은(접두의) 숫자는 16진수다. ex) 0x12f

이 방법으로는 0이 8진수인지 10진수인지 구분을 할 순 없지만 0은 어차피 0이기 때문에(8진수나 10진수나) 중요하지 않다.

비트 그룹의 이름

예전에는 12비트, 9비트 6비트 여러 비트 덩어리로 나뉘어 사용했지만 시간이 지남에 따라 세계적으로 8비트 단위가 기본 단위로 쓰이기 시작하며 8비트 단위를 바이트byte 라고 부르기 시작했다. 다른 크기의 덩어리들 또한 있다

이름 비트개수
니블(nibble) 4
바이트(byte) 8
하프 워드(half word) 16
워드(word) 32
더블 워드(double word) 64

워드word는 컴퓨터가 설계상 가장 자연스럽게 사용할 수 있는 비트 묶음(가장 빠르게 처리할 수 있다는 뜻이다)이다. C나 C++등의 int(integer, 정수를 뜻하는 영어다)라고 선언한 변수가 이런 자연스러운 크기의 2진수를 표현한다. 또 큰 수를 가리키기 위한 표준 용어도 존재하는데, 미터법에서 킬로는 1천, 메가는 100만, 기가는 10억, 테라는 1조를 뜻 하는데, 컴퓨터에서는 2의 거듭제곱으로 표현했다.

킬로바이트kilobyte는 1024($2^{10}$)
메가바이트megabyte는 $2^{20}$
기가바이트gigabyte는 $2^{30}$
테라바이트terabyte는 $2^{40}$의 크기이다.

그러나 밑의 크기가 다른 경우도 존재하는데, 이 점을 모른척하고 미국의 어느 변호사가 크기가 다르다는 점으로 소송을 걸었던 적도 있다고 한다. 이로 인해 새로운 IEC 표준 접두사가 만들어졌는데,

키비kibi는 $2^{10}$
메비mebi는 $2^{20}$
기비gibi는 $2^{30}$
테비tebi는 $2^{40}$ 을 뜻한다.

텍스트 표현

이렇게 수를 표현하는 방법을 여러 가지 알았는데 그렇다면 텍스트는 어떻게 컴퓨터로 표현할 수 있을까?

아스키 코드(ASCII CODE)

수와 마찬가지로 텍스트를 표현하는 방법의 경우에도 몇 가지가 존재하는데. 그 중 가장 유명하고 승자라고 할 수 있는 정보 교환을 위한 미국 표준 코드ASCII, American Standard Code for Information Interchange에 대해 알아보자. ASCII(이하 아스키)는 키보드에 있는 모든 기호에 7비트 값을 할당했다. 65는 대문자 ‘A’를, 66은 대문자 ‘B’를 표현하는 방식이다. 아스키 코드의 모든 문자를 보고싶다면 아스키 코드 테이블을 참고하자.

이 아스키 코드 테이블을 봤다면 재미있는 코드가 눈에 보였을 것이다. 제일 첫 번째부터 있는 놈들인데 NUL, STX, EOT 등의 문자이다. 이 놈들은 글자를 출력하는데 쓰이지 않고 장치를 제어하기 위해 쓰이기 때문에 제어 문자control character라고 불린다.

다른 표준

아스키는 ‘영어’를 표현하는데 필요한 모든 문자를 포함하고 있어 상당 기간 표준 역할을 했지만, 컴퓨터가 널리 쓰이게 됨에 따라 그 밖의 언어를 지원해야 했다. 그래서 국제 표준화 기구ISO, International Standards Organization은 유럽 언어의 악센트 기호와 그 밖의 발음 구별 기호를 더한 ISO-646ISO-8859를 도입했다. 일본에서도 일본 문자를 표현 하기 위해 JISX 0201을 만들었고, 한국에서 또한 KS C 5601등의 표준도 생겼다. 그 외 나라도 표준이 존재한다.

이렇게 많은 표준이 생겨난 데에는 유니코드Unicode가 한 몫을 했다. 비트가격이 비싸던 시절 표준이 만들어졌고, 문자를 7비트 or 8비트에 욱여넣었다. 하지만 비트가격이 떨어짐에 따라 유니코드라는 새로운 16비트 코드 표준이 만들어졌고, 지구상의 모든 문자를 담아도 여분이 남으리라 생각했던 것이다.

유니코드 변환 형식 8비트, UTF-8

아스키코드는 7비트 값을 처리하도록 설계되지 않았기 때문에 아스키코드를 저장하는데 현재에 16비트로 저장하는 것보다 8비트로 모든 문자를 표현할 수 있기 때문에 문자를 8비트로 표현한다. 유니코드는 문자 코드에 따라 각기 다른 인코딩을 사용하는데 인코딩Encoding이란 다른 비트 패턴을 표현하기 위한 비트 패턴을 뜻한다. 특히 켄 톰슨Ken Thompshon과 롭 파이크Rob Pike가 만든 유니코드 변환 형식 8비트UTF-8, Unicode Tranformation Format-8 bit 라는 인코딩 방식이 가장 호환이 잘 되고 효율적이기 때문에 가장 널리 쓰이고 있다.

그렇다면 어떻게 변환되는지 알아보자. UTF-8로 변환할 때는 해당 범위에 따라 왼쪽에 숫자를 부여하는 규칙에 따라 변환된다. 그 규칙은 다음과 같다.

0~127(0x0000 ~ 0x007f) : 0XXXXXXX
128~1247(0x0080 ~ 0x07FF) : 110XXXXX 10XXXXXX
2048~65535(0x0800 ~ 0xFFFF) : 1110XXXX 10XXXXXX 10XXXXXX

0~127 사이의 A, 128~2047 사이의 π, 2048~65535 사이의 ♣ 로 위의 범위에 대한 예제를 들어보겠다.

1
2
3
4
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0  0  0  0  0  0  0 0 0 1 0 0 0 0 0 1 유니코드 A (0x0041)
                        ↓ ↓ ↓ ↓ ↓ ↓ ↓ 
                      0 1 0 0 0 0 0 1 UTF-8 A (0x41)

처럼 문자의 코드가 0~127(16비트일 때 0x0000 ~ 0x007f) 사이라면 UTF-8 덩어리의 MSB를 0으로 설정해서 변환하고

1
2
3
4
5
6
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0  0  0  0  0  0  1 1 1 1 0 0 0 0 0 0 유니코드 π (0x03C0)
              [0  1 1 1 1][0 0 0 0 0]

7 6 5  4 3 2 1 0  | 7 6  5 4 3 2 1 0  UTF-8 π (0XCF 0X80)
1 1 0 [0 1 1 1 1] | 1 0 [0 0 0 0 0 0] 

처럼 문자의 코드가 128~2047(16비트일 때 0x0080 ~ 0x07ff) 사이라면 UTF-8 덩어리 두 개의 MSB부터 각각 110, 10으로 할당해 변환한다.

1
2
3
4
5
6
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0  0  1  0  0  1  1 0 0 1 1 0 0 0 1 1 유니코드 ♣ (0x2663)
[0 0  1  0][0  1  1 0 0 1][1 0 0 0 1 1]

7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 UTF-8 ♣ (0xE2 0X99 0XA3)
1 1 1 0[0 0 1 0]| 1 0[0 1 1 0 0 1]| 1 0[1 0 0 0 1 1]

처럼 문자의 코드가 2048~65535(16비트일 때 0x0800 ~ 0xFFFF) 사이라면 UTF-8 덩어리 세 개의 MSB부터 각각 1110, 10, 10으로 할당해 변환하는 방식이다.

문자를 사용한 수 표현

UTF-8은 A를 표현한다고 할 때 A를 표현하는 비트 2진수 0000000001000001로 나온 숫자0x0041을 인코딩한 값을 표현하기 위해 숫자 0x41을 사용한다.

A → 0000000001000001 → 0x0041 → 0x41

그렇다면 문자를 사용해 수를 표현할 수 있을까?

출력 가능하게 변경한 인코딩

출력 가능하게 변경한 인코딩Quoted-Printable encoding은 QP 인코딩 이라고도 하는데 8비트짜리 데이터를 7비트 데이터만 지원하는 통신 경로에 송수신하기 위한 인코딩 방법이다. 전자우편 첨부 처리를 위해 만들어졌고 이 인코딩을 사용하면 ‘=’ 다음에 바이트의 각 니블을 표현하는 16진 숫자 2개를 추가해 8비트 값을 표현한다. ‘=’가 기능을 가졌으므로 QP에서 ‘=’를 표현하려면 ‘=3D’를 사용하면 된다.

QP 인코딩에는 몇 가지 추가 규칙이 있는데 다음과 같다.

  • 줄의 맨 끝에 탭/공백 문자가 온다면 이는 ‘=09’ 와 ‘=20’으로 표현한다.
  • 인코딩된 데이터는 한 줄이 76자를 넘을 수 없다.
  • 줄의 맨 뒤가 ‘=’ 로 끝나면 가짜 줄 바꿈soft line break을 뜻한다. 수신쪽에서 이를 디코딩할 때 이 ‘=’를 제거하고 해석한다.

BASE64 인코딩

QP인코딩은 1바이트를 표현하기 위해 3바이트를 사용하기 때문에 작동은 잘 할지라도 효율은 떨어진다. 그에 비해 BASE64 인코딩은 더 효율적인데 지금보다 훨씬 느렸던 예전의 컴퓨터 속도를 생각하면 이런 효율성이 정말 중요했다. BASE64 인코딩은 3바이트 데이터를 4문자로 표현하는데 3바이트 데이터의 24비트(8, 8, 8)을 네 가지 6비트 덩어리로 나누고(6, 6, 6, 6) 각 덩어리의 6비트 값에 출력 가능한 문자를 할당해 표현한다.

BASE64 변환 표를 참고하자.

0, 1, 2라는 3바이트를 인코딩하면 AAEC가 된다(변환표 참고). 그리고 이 AAEC는 이렇게 변환된다.

1
2
3
4
5
┌─────────────────┬─────────────────┬─────────────────┐
│        0        │        1        │        2        │
│ 0 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 1 | 0 0 0 0 0 0 1 0 │
│     0(A)   │     0(A)    │     4(E)    │    2(C)    │
└────────────┴─────────────┴─────────────┴────────────┘

이 인코딩은 모든 3바이트를 4바이트 조합으로 바꿀 수 있지만 원본의 길이가 전부 3바이트의 배수라는 보장이 없기 때문에 패딩padding 이라는 문자를 도입해 이런 문제를 해결한다. 원본 데이터에 2바이트가 남으면 ‘=’를 붙이고 1바이트가 남으면 ‘==’를 붙여서 해결한다.

URL 인코딩

웹 페이지의 주소의 뒤에 %26, %2F 같은 문자 시퀀스를 본 적이 있을 것이다. 이런 것들이 있는 이유는 URL이라는 문맥에서 몇몇 문자들이 특별한 의미를 지니기 때문인데, 하지만 이런 특별한 의미를 지닌 몇몇 문자들도 리터럴literal로 사용할 필요가 있다(특별한 의미 없이 문자를 뜻하는 경우를 ‘리터럴’ 이라고 한다.).

URL 인코딩은 퍼센트 인코딩percent-encoding이라고도 하며 %뒤의 어떤 16진 표현을 덧붙이는 방식으로 인코딩한다.

*예를 들어, ‘ / ‘ 는 URL에서 의미가 있는 문자인데, 이 문자를 리터럴로 표현하기 위해서는 %2F를 쓴다.

색 인코딩

색을 표현하는 방법

고등학교의 어느 수학 문제에서, X축과 Y축을 가진 어느 그래프의 한 점을 나타내고 싶을 때는 그 점의(X값, Y값)으로 표현한다. 컴퓨터 그래픽스 에서는 이 점blob을 찍어 그림을 만드는데, 이 모눈의 격자에 찍는 점을 그림 원소picture element라고 부르고 줄여서 픽셀pixel이라고 한다. 또 색을 나타내고 싶을 때는 3원색인 빨강, 초록, 파랑의 (빨강 값, 초록 값, 파랑 값)을 넣어서 색을 나타낸다. 이런 색 표현법을 RGB 표현법Red Green Blue color model 이라고 한다. 0에 가까울수록 주 색의 빛을 약하게 한다는 것이고, 1에 가까울수록 주 색의 빛을 강하게 한다는 뜻이다. 이런식으로 빛을 혼합해 색을 표현하는 방식은 가산additive색 시스템 이라고 한다. 감산subtractive색 시스템은 주 색이 청록색(Cyan), 자홍색(Magenta), 노란색(Yellow)이다. 감산 색 시스템은 흰색 광선에서 각 색에 해당하지 않는 빛을 제거하며 색을 만들어내고 가산 색 시스템은 특정 빛의 광선을 서로 추가해 색을 만든다. 가산 시스템이 감산 시스템보다 더 많은 색을 만들어 낼 수 있다.

색 인코딩

웹 페이지는 주로 UTF-8문자의 텍스트text를 표현하기 때문에 텍스트를 사용해 색을 표현할 방법이 필요하다. 웹 페이지는 URL 인코딩과 비슷한 방법으로 색을 인코딩하는데 16진 트리플렛hex triplet이라는 멋들어진 이름으로 색을 표현한다. #뒤의 16진 숫자를 추가해 #rrggbb 처럼 표현하는 방식인데, rr은 빨강, gg는 녹색, bb는 파랑을 의미한다. 예를 들어 #ffff00 은 노란색, #000000 은 검은색의 방식이다.

정리

비트를 사용해 아주 큰 숫자나 문자, 색 등을 표현하는 방법, 10진수를 2진수로 표현하는 방법, 2진수의 사칙연산, 음수와 분수 표현 방법, 문자를 인코딩하는 표준 등을 배웠다.

댓글남기기