C & C++

C의 음수 표현법과 2의 보수

컴퓨터에서는 모든 수를 2진법으로 저장한다. 우리가 화면에 10이라고 십진수로 써도 실제로는 0000 1010이라고 저장된다는 것이다. 하지만 단순히 이런 방법으로만 하면 음수를 저장할 방법이 없다.

 

그래서 프로그래머들은 다음과 같은 방법을 고안했다. 바로 첫 번째 비트는 부호 (-, +)를 저장하고 그다음부터는 실질적인 값을 저장한다는 것이다. 맨 앞에 있는 비트 0은 '+', 1은 '-'를 의미한다고 보면 된다.

0000 0000 = +0

0000 0001 = +1

...

0111 1111 = +127

 

1000 0000 = -0

1000 0001 = -1

...

1111 1111 = -127

 

하지만 이렇면 문제점이 생긴다. 바로 0을 표현할 방법이 두 개라는 것인데, 그러면 사소하지만 메모리를 손해 보게 된다. 조금이라도 메모리를 줄이고 싶던 프로그래머들은 1000 0000을 0이 아닌 -128의 값으로 표현하자고 결정했다.

이렇게 하면 수를 하나 더 표현할 수는 있지만 조금 무언가 이상하다.

-1 = 1000 0001

-2 = 1000 0010

-3 = 1000 0011

...

-126 = 1111 1110

-127 = 1111 1111

-128 = 1000 0000 ?????

 

수의 순서가 잘 안 맞는 것을 확인할 수 있다. 또한 컴퓨터는 뺄셈을 할 수 없기 때문에, 뺄셈을 덧셈으로 변환해서 계산하는데 이렇게 하면 뺄셈을 할 수 없게 된다.

그런데 만약

1000 0000 = -128

1000 0001 = -127

1000 0010 = -126

...

1111 1101 = -3

1111 1110 = -2

1111 1111 = -1이라고 저장하면?

순서도 맞으며 이제는 뺄셈도 할 수 있다.

 

3 - 4를 하는 과정을 살펴보자.

일단 3 + (-4)로 변환하게 된다. 이진수로 표현하면

  0000 0011

+1111 1100

=1111 1111 = -1

우리가 십진수의 덧셈을 할 때처럼 그냥 더하기만 하면 된다.

 

정리하자면, 

B000 0110라는 이진수가 있다면

만약 B가 0이면 그냥 000 0110의 십진수 값인 6을 의미한다.

만약 B가 1이면 1000 0000 이 -128 이기 때문에 -128 으로부터 6번째 값인 -122를 의미한다.

쉽게 계산하려면 000 0110의 값을 계산 한 다음, -128에 더하면 쉽게 구할 수 있다. (8비트 정수일 때의 말이다. 더 크거나 작으면 -128이 아닌 다른 값에 더해야 한다.)

 

그래서 이러한 방식으로 음수를 저장하는 방식을 2의 보수법이라고 한다.

근데 왜 2의 보수법이라는 이름을 붙여놨을까? 

 

일단 보수라는 개념을 먼저 소개하자면, N의 보수라고 하면 어떤 수가 N이 되기 위해 보충을 해 줘야 하는 수를 의미한다. 7에 대한 10의 보수는 3, 16에 대한 36의 보수는 20인 셈이다.

 

대다수의 프로그래밍 언어에서는 2진수를 기반으로 하여 1의 보수와 2의 보수가 사용되어진다.

1의 보수란, 2진수의 각 자릿수가 1이 되기 위해 더해줘야 하는 수이다.

 

8비트 정수에서 1의 보수를 계산해보자. 0100 1011라는 수 가 있을 때, 모든 수가 1 되려면 1011 0100 을 더해야 하는 것을 알 수 있다. 어떤 수의 1의 보수는 모든 비트를 반대로 한 (1이면 0으로, 0이면 1로) 값과 같다. 

 

그럼 2의 보수는 어떻게 구할까?

아까 구했던 1의 보수에서 1을 더하면 바로 2의 보수이다.

75의 1의 보수는 1011 0100 이였으니까 2의 보수는 간단하게 1011 0101 이 되는 것이다.

 

이제 다시 음수를 표현한다고 가정하면, 75의 2의 보수를 계산하면 1011 0101이고 이것은 -75이다.

드디어 오늘의 결론에 도달했다. 컴퓨터에서 음수를 표현할 때, 어떤 수 N에 2의 보수는 -N 이였던 것이다.

 

마지막으로 보수와 밀접하게 관련된 한 연산자를 소개하려 한다. 바로 '~'이다. C에서 이 기호의 이름은 Bitwise Not이다. 직역하면 비트적 아님 정도가 되겠고, 어떤 수의 모든 비트를 반대로 한, 즉 어떤 수의 1의 보수를 계산한다.

~10 = ~0000 1010 =  1111 0101 = -11

~10 이 -10 이 아닌 -11 인 이유가 바로 ~는 1의 보수를 계산하지만 컴퓨터는 2의 보수를 취하기 때문이다. 따라서 (N이 양수 일 때) ~N의 값은 -(N+1) 이 되겠다.