이제 여러분들은 계산기를 만들 수 있어요 (진짜로)

지금까지 0과 1이라는 단순한 신호만으로도 세상의 모든 정보를 표현할 수 있음을 배웠습니다. 그리고 논리 게이트들을 조합해 0과 1로 표현된 정보로부터 새로운 정보를 만들어낼 수 있다는 것도 이해했죠.

이제 이론에서 벗어나 구체적인 예시를 만나볼 시간입니다. 오늘은 논리 게이트를 조합해서 실제로 덧셈을 수행하는 회로를 직접 만들어볼게요.

calculator

스마트폰의 계산기 앱은 사람보다 훨씬 빠르게 덧셈을 합니다. 999,999 + 888,888 같은 계산도 순식간에 해내죠. 그런데 그 계산기의 핵심도 결국 우리가 지금까지 배운 논리 게이트들의 조합일 뿐입니다.

이번 게시물을 마치고 나면 여러분도 논리 게이트를 조합해서 자신만의 계산기를 만들 수 있게 됩니다. 그것도 여러분 손으로 직접 계산하는 것보다 훨씬 빠르고 정확한 계산기를 말이에요!

초등학생 때 복습

컴퓨터가 어떻게 덧셈을 하는지 알아보기 전에, 우리가 평소에 어떻게 덧셈을 하는지 돌아봅시다. 예를 들어 123 + 456을 어떻게 계산하시나요?

  123
+ 456
-----
  579

우리는 자연스럽게 오른쪽부터 한 자리씩 계산합니다. 만약 한 자리에서 10이 넘는 결과가 나오면 올림을 해서 다음 자리에 더해줍니다.

올림이 있는 좀 더 복잡한 예를 봅시다. 179 + 287을 계산해보면:

  11  <-- (올림)
  179
+ 287
-----
  466
  1. 9 + 7 = 16이므로 6을 쓰고 1을 올림합니다.
  2. 1 + 7 + 8 = 16이므로 6을 쓰고 1을 올림합니다.
  3. 1 + 1 + 2 = 4입니다.

이 과정을 잘 보면 두 가지 핵심 요소가 있습니다:

바로 이 두 가지가 컴퓨터가 덧셈을 하는 핵심 아이디어입니다. 그런데 컴퓨터는 0과 1만 알고 있잖아요. 어떻게 이 원리를 0과 1의 세계로 옮길 수 있을까요?

0과 1로 숫자 표현하기

일상에서 우리는 총 10개의 숫자를 기본 단위로 삼는 십진법을 사용합니다. 십진법에서는 각 자리 숫자에 1, 10, 100, 1000처럼 점점 10배씩 커지는 값을 곱해서 전체 숫자를 만듭니다. 예를 들어, 숫자 123을 자세히 뜯어보면 이렇게 해석할 수 있어요:

1×100+2×10+3×1=1×102+2×101+3×100\textcolor{red}{1} \times 100 + \textcolor{blue}{2} \times 10 + \textcolor{green}{3} \times 1 \\ = \textcolor{red}{1} \times 10^2 + \textcolor{blue}{2} \times 10^1 + \textcolor{green}{3} \times 10^0

즉, 가장 왼쪽 자리는 100의 자리, 가운데는 10의 자리, 마지막은 1의 자리를 뜻하는 거죠. 자리값을 이용한 표현 방식입니다.

사실 컴퓨터도 똑같은 원리를 사용해요. 단지 숫자를 표현할 때 0과 1, 두 개의 숫자만 사용할 뿐입니다. 그래서 자리가 늘어날 수록 10배가 아닌 2배씩 커지는 값을 곱합니다. 이를 이진법라고 합니다.

예를 들어, 이진법으로 나타낸 이진수 1012을 십진수로 바꾸면 이렇게 됩니다:

1×22+0×21+1×20=1×4+0×2+1×1=4+0+1=510\textcolor{red}{1} \times 2^2 + \textcolor{blue}{0} \times 2^1 + \textcolor{green}{1} \times 2^0 \\ = \textcolor{red}{1} \times 4 + \textcolor{blue}{0} \times 2 + \textcolor{green}{1} \times 1 \\ = 4 + 0 + 1 = 5_{10}

아래에서 십진수를 이진수로 직접 변환해보세요.

1012
= 1 × 22 + 1 × 20
= 4+1=510

이렇게 이진수로 표현한 수를 논리 게이트들로 처리하면 새로운 의미있는 수를 만들 수 있습니다.

이번엔 이진수로 덧셈

먼저 간단한 예부터 시작해봅시다.

십진수 310 + 110 = 410를 이진수 버전으로 계산해보면 112 + 012 = 1002가 됩니다.

  11   <-- (올림)
   11   
+  01   
-----
  100   
  1. 1 + 1을 하면 십진수에서는 210지만 이진수로 표현하면 102입니다. 즉, 현재 자리는 0이 되고 다음 자리로 1을 올림합니다.
  2. 1(올림) + 1 + 0 = 102이므로 현재 자리는 0, 다음 자리로 1을 올림합니다.

결과적으로 100₂가 나오고, 이는 십진수 4와 같습니다.

이 과정에서 핵심은 십진수 때와 마찬가지로 각 자리에서 발생하는 두 가지 결과입니다:

반가산기

이제 본격적으로 덧셈 회로를 만들어봅시다. 한 자리 이진수 두 개를 더하는 가장 간단한 경우를 생각해봅시다.

가능한 모든 경우를 나열해보면:

0 + 0 = 0  
0 + 1 = 1  
1 + 0 = 1  
1 + 1 = ?

마지막 경우가 흥미롭습니다. 1 + 1은 102이 됩니다. 즉, 현재 자리 결과(Sum)는 0이고 다음 자리로 넘어가는 올림(Carry)은 1이 발생하는 거죠.

진리표를 보면 Sum은 A와 B가 다를 때만 1이 되고, Carry는 A와 B가 모두 1일 때만 1이 됩니다.

SumCarry
0000
0110
1010
1101

위의 진리표를 논리 게이트로 구현하면 한자리수 이진수 덧셈을 할 수 있는 계산기를 만든 것입니다!

벌써 한자리 덧셈 계산기가 만들어졌네요! 이걸 반가산기(half adder) 라고 합니다.

NAND로 직접 구현한 반가산기

사실 엄마 아빠가 둘 다 좋지는 않았던 컴퓨터의 내용을 참고해 위 회로를 NAND로 구현하면 아래와 같습니다.

전가산기

왜 반(half) 가산기일까요? 반가산기는 훌륭하지만 한 가지 문제가 있습니다. 실제로 여러 자리 숫자를 더할 때는 아래 자리에서 올라온 올림값도 함께 처리해야 하거든요.

앞서 계산했던 예제를 다시 봅시다:

  1    (올림)
  11   (십진수 3)
+ 01   (십진수 1)
----
 100   (십진수 4)

첫 자리를 계산할 때는 두 개의 입력만 고려하면 됐지만, 그 다음 자리부터는 이전 자리에서 올라온 올림값(Carry) 을 처리해주어야합니다. 위 예제에서는 2의 자리를 계산할 때 1+1+0을 했지요.

이처럼 세 개의 입력값(A, B, 그리고 아래 자리에서 올라온 Carry)을 모두 처리할 수 있는 회로가 필요합니다. 이를 전가산기(full adder) 라고 합니다.

전가산기의 진리표는 다음과 같습니다. 구분을 위해 이전 자리에서 올라온 올림값을 Cin으로, 다음 자리로 넘어가는 올림값을 Cout이라고 합니다.

CoutSum
00000
00101
01001
01110
10001
10110
11010
11111

복잡해보이지만 각 행을 이진수 덧셈이라고 생각해보세요. 예를 들어 마지막 행은 1 + 1 + 1 = 11₂이므로 Sum은 1, Cout은 1이 됩니다.

전가산기는 반가산기와 OR 게이트를 활용해 만들 수 있습니다.

여러자리 덧셈

전가산기 하나로는 1비트 덧셈만 가능합니다. 그럼 4비트나 8비트처럼 여러 자리 숫자를 더하려면 어떻게 해야 할까요?

자릿수가 바뀐다고 덧셈의 방식이 바뀌는 것은 아니니 단순히 전가산기 여러 개를 차례대로 연결하면 됩니다! 각 자리마다 하나의 전가산기를 배치하고, 한 자리의 Carry-out을 다음 자리의 Carry-in으로 연결하는 거죠.

전가산기 10개면 1000이 넘는, 30개면 10억이 넘는 수까지의 덧셈도 가능합니다! 아래 예제에서는 1112 + 12 = 10002를 계산합니다.

이런 방식을 리플 캐리 가산기라고 부릅니다. 올림이 물결(ripple)처럼 다음 자리로 차례차례 전파되기 때문이에요.

조합 논리

드디어 논리 게이트들을 조합해서 실제로 숫자 덧셈을 수행하는 회로를 만들었어요!

참고로, 덧셈뿐만 아니라 뺄셈과 곱셈도 모두 논리 게이트의 조합으로 만들 수 있어요.

여기서 만든 가산기는 조합 논리(Combinational Logic) 의 대표적인 예입니다. 조합 논리회로란 현재 입력값에만 의존해서 출력이 즉시 결정되는 회로를 말해요. 즉, 과거의 입력이나 상태는 전혀 영향을 주지 않고, 오직 지금 주어진 입력값만을 바탕으로 결과가 나오는 거죠.

가산기의 경우, 입력된 두 비트(또는 세 비트, 전가산기에서는 Cin 포함)를 받아서 즉시 Sum과 Carry를 출력합니다. 입력이 바뀌면 출력도 바로 바뀌고, 이전에 어떤 입력이 들어왔었는지는 기억하지 못해요. 이처럼 조합 논리는 기억 없이 계산만 하는 회로라고 볼 수 있습니다.

마무리

한 가지 문제가 남아있습니다. 지금까지 만든 회로들은 모두 계산 결과를 기억하지 못해요. 계산이 끝나면 결과가 사라져버리죠. 실제 컴퓨터가 되려면 정보를 저장하고 기억할 수 있어야 합니다.

예를 들어, 1000번째 피보나치 수를 계산하고 싶다고 해봅시다. 피보나치 수열은 앞의 두 값을 계속 더해서 다음 값을 만들어가는 구조인데, 이전 계산 결과를 하나도 기억하지 못한다면 매번 처음부터 다시 계산해야 하겠죠. 그만큼 비효율적이고, 실질적인 프로그램도 만들 수 없게 됩니다.

다음 게시물에서는 이 문제를 해결해보겠습니다. 논리 게이트를 이용해서 정보를 저장하는 회로를 만들고, 메모리의 기본 원리를 이해해봅시다. 드디어 컴퓨터가 ‘기억’을 갖게 되는 순간을 살펴보시죠!

이전글목록으로

로그인 중...

seongyeol