주인장의 프로그래밍 개발 공부방

[Chapter 7 컴퓨터 구조] 2. 2진법의 음수 표현 (2편 - 1의 보수, 2의 보수) 본문

프로그래밍 기본 지식/Chapter 7 컴퓨터 구조

[Chapter 7 컴퓨터 구조] 2. 2진법의 음수 표현 (2편 - 1의 보수, 2의 보수)

거신 2022. 9. 14. 16:34

1편에서는 2진수를 음수로 표현하는 방법 중 부호 절댓값에 대해서 간단하게 알아봤다.

2편에서는 보수라는 개념을 통해서 1의 보수와 2의 보수를 표현하는 방법,

2의 보수로 2진수를 음수로 표현하는 방법이 효과적인 이유를 설명한다.

 

목표 : 보수의 개념을 이해하고 1의 보수와 2의 보수를 이용해 음수로 변환 후 음수 연산을 할 수 있다.

 

1. 보수의 개념

 

1편에서 부호 절댓값에 대해서 알아봤는데 음수로 변환하는 방법은 간단하지만

그에 따른 단점이 있는 것을 알 수 있었다.

결국 우리와 함께 갈 수 없었던 부호 절댓값을 뒤로하고

우리 앞에 등장한 녀석이 바로 보수라는 개념이다.

 

생소한 단어일 수도 있는데 수학에서 등장하는 용어인데 수학적 의미로는

"보충해주는 수, 합해서 특정 숫자를 채우게 하는데 필요한 수"

라는 뜻인데 이를 컴퓨터 공학에서 보면

"어떤 수에 합해서 해당 진법의 밑수로 만들어 주는 수"

라는 복잡한 의미로 설명할 수 있다.

 

좀 더 풀어서 설명하자면

10진법을 기준으로 예를 들자면

3이라는 숫자의 보수는 더했을 때 10진법의 밑수인 10으로 만들어주는 수인

7이 3의 보수가 되는 것이다.

이를 10의 보수라고 표현할 수 있다.

사실 조금 더 수학적인 부분에서 자세히 설명하면 좋은 부분이 있으나

글이 길어질 수도 있고 지금은 우리가 필요한 부분을 중점적으로 알아가 보자.

 

보수라는 수학적 개념을 간단하게 알아봤으니 이제 본격적으로 1의 보수2의 보수를 알아보도록 하자.

 

2. 1의 보수

 

우리는 2진법을 사용하고 0과 1만을 사용하기 때문에

보수를 이용하는 데 있어서 좀 더 간편하게 이해할 수 있다는 장점이 있다.

그런데 1의 보수는 무엇이며, 2의 보수는 무엇이냐 하면

일반적으로 n진법에서의 보수(n-1)의 보수, n의 보수 이렇게 2가지로 사용할 수 있다는 특징이 있다.

그렇기 때문에 2진법에서는 1의 보수와, 2의 보수가 존재하고 있는 것이다.

 

1의 보수는 앞의 보수 개념을 이용하면 어떤 수를 더했을 때 1이 되게 해주는 수를 말하는 것일 테고

2진법에서는 0과 1만 존재하므로 두 수가 각각의 1의 보수가 된다는 것을 유추할 수 있을 것이다.

간단히 말해 1 -> 0, 0 -> 1로 바꿔주기만 하면 된다.

 

예시 1)

0011 1001 (2)

1100 0110 (2) <- 1의 보수

 

예시 1)과 같이 단순히 0과 1을 바꿔주기만 하면 되기 때문에 간단하게 1의 보수를 구할 수 있다.

 

※ 다른 곳에서 1의 보수의 설명에서 대부분 뺄셈식도 보여주면서 설명을 하는데 여기에서는

뺄셈식은 '가능하면' 포함하지 않고 설명하려고 한다.

 

그러면 이제 1의 보수를 구할 수 있게 되었으니 연산을 통해서 확인해보자.

 

0011 1001 (2)는 10진수로 57 (10)이고

이에 1의 보수

1100 0110 (2)로 표현할 수 있고 -57 (10)이라는 것을 알 수 있다.

 

여기에 88 (10)을 더해보자.

 

예시 2)

1100 0110 (2)

0101 1000 (2)

-57   1 1 0 0 0 1 1 0
88 + 0 1 0 1 1 0 0 0
1 0 0 0 1 1 1 1 0

이렇게 연산을 하면 자릿수에서 오버플로우가 발생한 캐리 값이 발생하게 된다.

일단 캐리 값을 무시하고 결괏값만 보면

 

0001 1110 (2)로 30 (10)

 

이 나오는데 정확한 값인 31에서 1이 모자란 결과가 나온다.

여기에서 바로 캐리 값을 결괏값에 더해주면 된다.

 

그러면 최종 결괏값은

31 0 0 0 1 1 1 1 1

31 (10)이 도출되는 것을 알 수 있다.

정리하자면 1의 보수로 인해서 캐리 값이 발생했으므로 연산된 결괏값에 캐리 값을 더해주면 되는 것이다.

 

그렇다면 캐리 값이 발생하지 않는 경우도 있을까?

아래의 예시를 보자.

 

예시 3)

1100 0110 (2)

0000 1010 (2)

-57   1 1 0 0 0 1 1 0
10 + 0 0 0 0 1 0 1 0
  1 1 0 1 0 0 0 0

위에서 연산된 결과는 MSB가 1이므로 부호는 - (마이너스)이므로 보수로 되어 있다는 것을 알 수 있다.

따라서 1의 보수로 바꾸면

47 0 0 1 0 1 1 1 1

47 (10)이 되는 것을 확인할 수 있다.

마지막으로 원래 결과의 부호가 - 였으므로

최종 결과는 -47 (10)로 캐리 값이 발생하지 않는 것도 확인할 수 있다.

 

이는 1의 보수의 단점을 보여주고 있는 것이기도 하다.

오버플로우로 인해 캐리 값이 발생하게 되면 추가적인 연산 작업이라는 수고스러움이 발생하게 되고

이는 논리회로의 설계를 복잡하게 만든다는 문제가 발생하게 된다.

 

또한 1의 보수도 앞서 알아봤던 부호 절댓값과 마찬가지로 MSB부호 비트로 사용하면서

0을 표현하는 데 -0과 +0으로 구분하여 표현한다는 점도 동일하다.

 

하지만 해당 비트를 반전 시키는 것만으로 음수를 표현할 수 있고

덧셈만으로 뺄셈을 구현할 수 있다는 특징이 있다.

 

이러한 장단점을 통해 우리는 1의 보수에서 조금 더 개선된

2의 보수라는 녀석을 만나러 갈 것이다.

그럴거면 뭐하러 굳이 이렇게 길게 1의 보수라는 것에 대해서 알아봐야 했는가 의문이 들 수도 있을텐데

그 이유는 2의 보수1의 보수의 개념이 필요하기 때문에

우리는 지금까지 그 준비 운동을 한 것이라고 생각해주면 좋겠다.

 

3. 2의 보수

 

이제 본격적으로 2진법의 세상에서 음수를 표현하는 방법과 2의 보수에 대해서 자세히 알아보도록 하자.

 

2의 보수는 말 그대로 어떤 수에 더했을 때 밑수 2가 되게 해주는 수라는 것을 이제는 바로 이해할 것이다.

그런데 1의 2의 보수는 1이라는 건 당연히 알겠지만 0의 2의 보수는 무엇이란 말인가?

2진법의 세계에서는 0과 1이라는 숫자 밖에 존재하지 않는데

0의 2의 보수인 수 2는 도대체 어디에 있다는 것인가.

 

앞서 보수의 개념의 이해를 돕기 위해 10진법을 이용해 간단하게 설명했었다.

조금 당연한 소리를 하자면 10진법에서 표현할 수 있는 수는

1 ~ 10이 아니라 0 ~ 9라는 것을 알고 있을 것이다.

보수는 더했을 때 밑수가 되어야 하니 10진법에서도 0의 10의 보수는 10이 되어야 한다고 생각하겠지만

사실 0의 10의 보수는 0이 된다.

10진법에서 10은 자릿수를 넘어가는 수가 되기 때문에 앞자리에 오버플로우가 발생하고

그 결과 0이 되기 때문에 결국 보수의 개념에서는 10 == 0 이 된다는 뜻이다.

따라서 0의 10의 보수는 0이 된다는 것도 이해가 될 것이다.

 

※ 다시 말하지만 보수는 수학적인 개념이 필요하기 때문에 좀 더 정확하고 깊이 들어가야 하지만

여기에서 그러기엔 너무 어렵고 복잡하기 때문에 우리는 좀 더 간단하게 이해하자.

 

다시 2진법의 세계로 돌아와 2의 보수를 다시 살펴보면

0과 1로만 구성되어 있는 세계에서 2라는 숫자는 오버플로우가 발생한 숫자가 되는 것이고

결국 2 == 0이라는 것을 아마 이해할 수 있을 것이다.

즉, 0의 2의 보수는 2이면서 동시에 0인 것이다.

 

여기에서 오버플로우라는 것에 초점을 맞춰서 생각해야 한다.

2의 보수 등을 다루는 여러 블로그의 글들을 찾아보면 2의 보수를 만드는 방법을

원래 수를 1의 보수로 바꾸고 그 수에 1을 더하면 된다고 간단하게 설명하는 것을 볼 수 있을 것이다.

이렇게만 이해하고 사용한다면 변환 방법만 이해하는 것이기 때문에

여기에서는 단순히 변환 방법만 다루지 말고

왜 그렇게 되는 건지에 대한 부분을 조금 더 세밀하게 알아보자.

 

0의 10의 보수, 2의 보수가 왜 0이 되는지 알아보는 과정에서

자릿 수를 넘어가는 수, 다시 말해, 오버플로우가 발생하기 때문에

그 결과 0이 되는 것이고 0의 10의 보수, 2의 보수가 전부 0인 이유인 것이다.

조금만 더 생각해보면 보수는 단순히 보충해 주는 수가 아니라

 

"어떤 수에 합해서 (해당 진법의 밑수로 만들어 "오버플로우"를 시킨 후) 0으로 만들어주는 수"

 

라고 정리할 수 있겠다.

그렇다면 이를 바탕으로 바로 예시로 들어가 보자.

 

예시 4)

0 0 1 1 1 0 1 0

이라는 수가 있다면 이 수의 2의 보수를 구하기 위해선

더했을 때 오버플로우를 발생시켜 0으로 만드는 수를 찾으면 되는 것이다.

그 말인즉슨, 오버플로우 된 수에서 원래의 수를 "빼면" 우리가 찾고자 하는 2의 보수를 만날 수 있다는 뜻이기도 하다.

물론 컴퓨터 연산에서는 뺄셈 연산이 존재하지 않기 때문에

(그 때문에 이렇게 장황하게 보수를 공부하고 있기도 하고)

실제로는 존재하지 않지만 우리는 수학적인 접근으로 이해하기 위해서 뺄셈 연산을 하는 것이다.

 

8비트에서 오버플로우로 0이 되는 수는 아래와 같다.

1 0 0 0 0 0 0 0 0

오버플로우 수에서 예시 4)를 빼면

1 0 0 0 0 0 0 0 0
- 0 0 1 1 1 0 1 0
= 1 1 0 0 0 1 1 0

(2진법 뺄셈 헷갈린다면 인터넷에서 꼭 찾아보길...)

 

이렇게 계산한 결과

1100 0110 (2)

를 얻을 수 있다.

 

이제 2의 보수로 변환하는 방법으로 위의 값과 비교해 보자.

2의 보수로 변환하는 방법은 원래 수를 1의 보수로 변환한 후 +1을 하면 된다.

 

예시 5)

원래 수 0 0 1 1 1 0 1 0
1의 보수화 1 1 0 0 0 1 0 1

1의 보수로 변환한 예시 5)에 1을 더하면

1100 0110 (2)

가 되므로 우리가 앞서 계산했던 결과와 동일하다는 것을 알 수 있다.

 

이제 마지막으로 2의 보수로 연산을 해보자.

예시 4)2의 보수로 변환한 수

1100 0110 (2) ( == -58 (10) )

0101 1000 (2) ( == 88 (10) )을 연산해서 결과를 얻어보자.

 

예시 6)

-58   1 1 0 0 0 1 1 0
88 + 0 1 0 1 1 0 0 0
30 1 0 0 0 1 1 1 1 0

예시 6)의 결과와 같이 2의 보수로 연산한 결과는 1의 보수와는 달리

오버플로우가 발생해 이후에 캐리 값을 더해주지 않아도 정확한 값이 도출된다는 것을 알 수 있다.

 

이러한 특징들을 정리하자면 2의 보수에는 이런 장점들이 있다고 할 수 있다.

 

2의 보수캐리 값이 발생하더라도 추가적으로 더하는 연산이 필요 없기 때문에

1의 보수보다 논리회로 설계가 훨씬 간단해진다.

더군다나 캐리 값을 무시해도 되니 연산 속도도 빨라진다는 장점이 있다.

 

또한 2의 보수MSB를 부호 비트로 사용하면서도

부호 절댓값1의 보수의 단점이었던 -0, +0으로 구분하지 않고

하나의 0만을 사용하는 것도 장점이라 할 수 있겠다.

 

어쨌든 2의 보수로 변환하는 방법을 외우는 것이 더 효과적이지만

이 글을 통해서 어떻게 그런 변환 방법이 나타나게 되었는지에 대해서 알아가는 시간이 되었으면 한다.

 

3편에서 계속

Comments