컴퓨터를 포함한 각종 논리회로에서 음수를 표현하는 방법은 다음 4가지가 있다.

  1. 부호 절대값 (Sign-Magnitude)
  2. 1의 보수 (1's Complement)
  3. 2의 보수 (2's Complement)
  4. excess - n

그 외에 양수만 표현하는 unsigned 방식도 있다.

그런데 실제로 널리 사용되는 것은 2의 보수 방식이다. 이유는 회로가 간단해지기 때문이다. 위의 세가지 방식을 각각 살펴보면서 장·단점을 비교해 보자.


1. 부호 절대값 (Sign-Magnitude)


부호 절대값 방식은 가장 쉽게 생각할 수 있는 방식이다. MSB(최상위비트)을 부호비트(0이면 양수, 1이면 음수)로 사용하고, 나머지는 절대값을 표현한다. 4비트 정수를 예로 들면,

0000 +0 1000 -0
0001 1 1001 -1
0010 2 1010 -2
0011 3 1011 -3
0100 4 1100 -4
0101 5 1101 -5
0110 6 1110 -6
0111 7 1111 -7

그런데 부호 절대값 방식에는 몇 가지 문제가 있다. 우선 0이 두 개(+0과 -0)나 존재하기 때문에, 둘 다 0으로 인식하도록 해야 한다. 심각한 문제는 덧셈과 뺄셈을 할 때이다. 인간이 직접 계산할 때처럼 부호와 절대값을 따로 분리해서 계산해야 하고, 음수를 더함으로써 뺄셈을 구현할 수가 없어서 뺄셈기를 따로 구현해야 한다. 그러면 인간이 덧셈과 뺄셈을 수행할 때의 과정을 살펴보자.

2(10) + 3(10) = 0 010 + 0 011 = 0 101 = 5(10)

(양수끼리 더했으므로 양수) <덧셈기 사용>

-2(10) + -3(10) = 1 010 + 1 011 = 1 101 = -5(10)

(음수끼리 더했으므로 음수) <덧셈기 사용>


3(10) - 2(10) = 0 011 - 1 010 = 0 011 = 1(10)

(절대값이 큰 수에서 작은 수를 뺐으므로 양수) <뺄셈기 사용>

2(10) - 3(10) = 0 010 - 1 011 = -(011 - 010) = 1 001 = -1(10)

(절대값이 작은 수에서 큰 수를 뺄 경우에는 순서를 바꿔서 빼고 결과는 음수) <뺄셈기 사용>

인간에게는 매우 쉬운 일이지만, 위의 모든 사항을 고려하여 계산을 수행하는 회로를 만드는 것은 쉬운 일이 아니다. 그리고 <, >, <=, >= 등의 비교연산을 수행할 때 다음과 같은 모순이 생긴다.

3(10) > 2(10) = 0011 > 0010 = TRUE
-2(10) > -3(10) = 1010 > 1011 = FALSE (???)

위와 같이 음수의 경우는 반대가 되므로 이를 구분해야 한다. 사실 비교연산은 뺄셈을 한 후 결과의 부호를 가지고 판단하기 때문에 이는 덧셈과 뺄셈 문제와 동일하다.


2. 1의 보수 (1's Complement)


1의 보수 방식은 부호 절대값 방식에서 단순히 음수의 순서를 뒤집은 것이다.

0000 +0 1000 -7
0001 1 1001 -6
0010 2 1010 -5
0011 3 1011 -4
0100 4 1100 -3
0101 5 1101 -2
0110 6 1110 -1
0111 7 1111 -0

음수의 순서를 뒤집기만 한 것이라 별것 아닌것 같지만 사실 엄청난 유리함이 있다. 우선 재미 있는 성질이 하나 생긴다. 비트를 반전시키면 부호가 바뀌게 된다. 다행히 비트반전은 쉽게 구현할 수 있다. 그리고 MSB가 0이면 양수, 1이면 음수라는 성질은 그대로 유지된다.

3(10) = 0011 >> 1100 = -3(10)
-6(10) = 1001 >> 0110 = 6(10)

뿐만 아니라 부호 절대값 방식에서 골치거리였던 것이 깔끔하게 해결된다. 부호와 절대값을 따로 계산하지 않아도 되고, 음수를 더하는 방식으로 뺄셈을 할 수 있게 된다. 단, 캐리가 발생하면 LSB(최하위비트)에 1을 더해줘야 한다.

2(10) + 3(10) = 0010 + 0011 = 0101 = 5(10) <덧셈기 사용>
-2(10) + -3(10) = 1101 + 1100 = 1001 + 0001 = 1010 = -5(10) (캐리 발생) <덧셈기 사용>
3(10) - 2(10) = 0011 + 1101 = 0000 + 0001 = 0001 = 1(10) (캐리 발생) <덧셈기 사용>
2(10) - 3(10) = 0010 + 1100 = 1110 = -1(10) <덧셈기 사용>

어째튼 덧셈과 뺄셈이 매우 간단해졌다. 음수의 비교연산 모순도 해결되었다.

3(10) > 2(10) = 0011 > 0010 = TRUE
-2(10) > -3(10) = 1101 > 1100 = TRUE

그런데 여전히 0이 두 개인 것과 캐리를 처리해야 하는 문제가 남아있다. 즉, 0000과 1111을 둘 다 0으로 처리해야 하고, 계산과정에 캐리가 발생됐는지 감시해서 LSB에 1을 더해주는 회로를 구성해야 한다.

3(10) - 3(10) = 0011 + 1100 = 1111 >> 0000

게다가 위와 같이 실제 계산과정에서 발생하는 0은 항상 -0인 1111이다.

3. 2의 보수 (2's Complement)

2의 보수 방식도 단순해서 그저 눈에 거슬리는 -0을 없애기만 한 것이다.

0000 0 1000 -8
0001 1 1001 -7
0010 2 1010 -6
0011 3 1011 -5
0100 4 1100 -4
0101 5 1101 -3
0110 6 1110 -2
0111 7 1111 -1

-0이 없어지고 대신 -8(10)이 등장한 것 외에는 별것 아닌것 같지만, 1의 보수 방식의 골치꺼리였던 -0 문제와 캐리를 처리해야 하는 문제가 사라진다. 2의 보수 방식에서는 부호를 바꾸려면 비트를 반전한 다음 LSB에 1을 더하면 된다.

3(10) = 0011 >> 1100 + 0001 = 1101 = -3(10)
-6(10) = 1010 >> 0101 + 0001 = 0110 = 6(10)

-0는 아예 없으니 이미 해결됐고, 이제 캐리를 처리하지 않아도 되는지 직접 계산해보자.

2(10) + 3(10) = 0010 + 0011 = 0101 = 5(10)
-2(10) + -3(10) = 1110 + 1101 = 1011 = -5(10) (캐리 발생)
3(10) - 2(10) = 0011 + 1110 = 0001 = 1(10) (캐리 발생)
2(10) - 3(10) = 0010 + 1101 = 1111 = -1(10)

위에서 캐리가 발생했을 때, LSB에 1을 더해주지 않아도 결과가 정확한 것을 확인할 수 있다. 이제 덧셈이든 뺄셈이든 그냥 더하기만 하면 된다. 이제 구현해야 하는 회로가 무척 간단해졌음을 느낄 것이다.

2의 보수 방식의 장점을 요약하면 다음과 같다.

  • MSB가 0이면 양수, 1이면 음수라는 성질이 유지된다.
  • 음수를 더하는 방식으로 뺄셈을 할 수 있다.
  • 음수의 비교연산에서 발생하는 모순이 해결된다.
  • 0이 두개나 존재하는 모순이 해결된다.
  • 덧셈과 뺄셈을 구현할 때 캐리를 처리하지 않아도 된다.

3. unsigned

이 방식은 크게 설명이 필요없을듯 하다.

예를 들어 4비트 일경우 부호비트를 신경쓰지 않고 2진수를 10진수로 바꿔주면

그것이 unsigned의 값이 된다.

일단 사람 입장에서는 가장 직관적인 표현방식이다.

0000 0

0001 1

0010 2
0011 3

0100 4
0101 5

0110 6

0111 7

1000 8

1001 9

1010 10

1011 11

1100 12

1101 13

1110 14

1111 15

위와 같은 순서다. 더이상 무슨 설명이 필요하랴?

3. excess - 7

대형 컴퓨터에서 많이 사용하는 방식이다.

excess 뒤에 붙는 숫자는 굳이 고정된것이 아니라 연산 가능한 비트수에 따라 달라진다.

위의 2의 보수와 같이 4비트 일때는 7이고 비트수가 올라가면 그 만큼 올라가게 된다.

이 앞의 방식은 앞의 부호비트가 0일때 양수였지만 excess - 7 방식은 부호비트가 0일때

음수를 표현한다.

0000 -7 1000 1
0001 -6 1001 2
0010 -5 1010 3
0011 -4 1011 4
0100 -3 1100 5
0101 -2 1101 6
0110 -1 1110 7
0111 0 1111 8

바로 위와 같이 표현한다.

-7에서 부터 줄어들었다가 다시 8까지 증가한다.

표현 범위는 0이 하나이기 때문에 1의 보수나 2의 보수와 같지만

-8대신 양수 8이 존재한다.

처음에는 복잡하게 느껴질수 있지만 나중에 가면 오히려 계산하기가 편해진다.

계산방법은 단순하다 unsigned에 해당하는 2진수 코드에 7을 빼주기만 하면된다.

여기서 7을 빼는 이유는 excess -7 방식이기 때문이고 당연히 excess -127 방식이면 127을

빼야한다. excess -7이 마이너스 부호를 뜻하는건지 그냥 Dash인지는 모르겠다.

예를들어 0000 일때 unsinged의 값은 0이다. 여기서 7을 빼면 -7이다.

즉 excess -7 방식일때 코드 0000의 값은 -7이다.

excess -7 방식에서 0을 표현하고자 한다면 반대로 0에 7을 더한 unsigned 코드를 만들면 된다.

그 값은 0111이다.

약간 햇갈릴수도 있는데 정리하자면

excess - 7 방식의 코드를 봤는데 그코드를 10진수로 변환해야 한다면 그 코드의 unsigned 값을

계산하고 거기에 -7을 해주면 된다.

어느 정수를 excess -7 방식의 코드로 변환하고자 한다면 그 정수에 7을 더한후 2진수 코드로 변환하면

된다.

이런 excess 방식을 이용하는 컴퓨터 중에서 drop-bit를 사용하는 컴퓨터도 있는데

거기에 대해서는 다음에 설명하겠다.

마지막으로 아래 도표는 위에 나온 모든 방식의 코드 변환표이다.

위의 내용과 함께 보면 아마 이해가 더 빨라질것이다.

Code

The Represented Number

4-bit

word

unsigned

sign -

magnitude

1's

complement

2's

complement

excess -7

0000

0

0

0

0

-7

0001

1

1

1

1

-6

0010

2

2

2

2

-5

0011

3

3

3

3

-4

0100

4

4

4

4

-3

0101

5

5

5

5

-2

0110

6

6

6

6

-1

0111

7

7

7

7

0

1000

8

-0

-7

-8

1

1001

9

-1

-6

-7

2

1010

10

-2

-5

-6

3

1011

11

-3

-4

-5

4

1100

12

-4

-3

-4

5

1101

13

-5

-2

-3

6

1110

14

-6

-1

-2

7

1111

15

-7

-0

-1

8

---------------------------------------------------------------------------------------------------

출처 : http://blog.naver.com/dreamincalm/130081559335

정리가 너무 잘되어 있어서 그냥 수정없이 그대로 옮겨옴

2010.5.23 원본에서 excess -7과 unsinged에 대한 내용을 추가

'IT > Legacy' 카테고리의 다른 글

한글 putty 다운로드  (0) 2013.01.11
Linux vi편집기 명령어  (0) 2013.01.11
Fedora Core 4 자바 설치하기  (0) 2013.01.11
OpenCV 설치 하기  (0) 2013.01.11
각종 진법 변환(2진수 실수끼리의 연산, 실수의 진법변환 등)  (0) 2013.01.11

각종 진법 변환(2진수 실수끼리의 연산, 실수의 진법변환 등)

>> 2진수를 10진수로 :

10101 = 21

이런 결과가 나오는 이유는

(1 * 2^4) + (0 * 2^3) + (1 * 2^2) + (0 * 2^1) + (0 * 2^0) = 16 + 4 + 1 이다.

여기서 2^4는 2의 4승을 뜻한다. 2의 0승부터 시작해서 마지막 1이 나올때까지 계속 그 승을 올려주면서

곱해주고 그 곱한수들을 합산한다.

이런 계산법을 이용하면 다른 진법도 쉽게 구할수 있다

예를 들어서 3진수를 10진수로 변환한다면

------------------------------------------------------------------------------------------

>> 3진수를 10진수로 :

101(3bit) = 10

(1 * 3^2) + (0 * 3^1) + (1 * 3^0) = 9 + 1 이다.

------------------------------------------------------------------------------------------

>> 10진수를 2진수로 :

그러면 반대로 10진수를 2진수로 바꾼다면?

>> 10진수를 2진수로 :

54 = 110110

54/2 ...... 0

27/2....... 1

13/2....... 1

6/2......... 0

3/2......... 1

1/2......... 1

0

이런식으로 계산을 하고 아래서 부터 합산을 한다.

0110110이 나오는데 제일 앞의 쓸모없는 0을 빼면 110110 이다.

위의 2진수 -> 10진수 연산을 해보면 다시 54가 나오는것을 알수있다.

3진수나 5진수 7진수 기타등등 이런식으로 하면 된다.

이제 부터가 진짜 문제다

------------------------------------------------------------------------------------------

>> 실수형(float) 2진수를 10진수로 :

2진수 실수 0.101을 가지고 계산해 본다.

0.101(2bit) : 2진수 실수 0.101 이란 뜻이다.

아무튼 0.101(2bit)를 10진수로 변환하고자 한다면 두가지 방법이 있다.

십진수를 이용하는 방법과 이진수를 이용하는 방법이다.

우선 십진수를 이용 하는 방법을 사용한다.

이 방법은 division method 라고 한다.

------------------------------------------------------------------------------------------

>> division method : 2bit -> 10bit

0.101(2bit) =

( 0 + 1) / 2 = 0.5

> 여기서 0 + 1의 0 은 처음 시작은 그냥 0부터 시작한다고 외워라

> 0 + 1의 1은 소수 세번째 자리다. 뒤에서 부터 끌어온다.

> /2를 하는 이유는 2진법 이니까

(0.5 + 0) / 2 = 0.25

>여기서 0.5는 앞전 연산의 0.5가 온다.

>0은 소수 두번째 자리수다. 위에서 소수 첫번째 자리를 나눠줬고 순서대로 내려온다.

(0.25 + 1) / 2 = 0.625

> 이렇게 답 0.625가 나온다. 검산은 나중에 하겠다.

하는김에 0.001(2bit)도 해보자

(0 + 1) / 2 = 0.5

(0.5 + 0)/ 2 = 0.25

(0.25 + 0)/ 2 = 0.125 가 나온다.

10진수를 이용한 division method를 이용했으니 이제 2진수를 이용해보겠다.

2진수를 이용한 방법은 multiplication method 라고 한다.

multiplication method를 사용하기전에 우선 2진수 끼리의 곱과 나눗셈을 익혀야 한다.

------------------------------------------------------------------------------------------

>> 2진수 끼리의 곱

0101 * 1010

0 1 0 1

* 1 0 1 0

---------

0 0 0 0

0 1 0 1

0 0 0 0

0 1 0 1

------------

0 1 1 0 0 1 0 = 110010

이 나온다. 여기서 계산법은 0과 1이 만나면 0, 0과 0이 만나도 0, 1과 1이 만나면 1이 되는것이고

그렇게 각 자리수별로 산출한후에 더해주면 되는것이다. 2진수니까 더할때 1과 1이 겹치면 한칸위로

올라가는건 당연한 것이고 C를 하는 사람은 그냥 & 라고 생각하면 되겠다.

검산을 위해 위의 2진수들을 10진수로 변환해보면 5 * 10 = 50이 된다.

그러면 애초에 이런 귀찮은 연산 할것없이 10진수로 변환해서 사용하면 되지 않겠냐고 할것이다.

맞는말이다. 2진수 계산은 사람이 하기에는 실수(error)가 너무 많아서 사용하기가 힘들다.

하지만 컴퓨터는 10진수 계산을 할수가 없다. 그리고 정확하다. 이 계산법을 컴퓨터를 위한것이다.

아무튼 만약 정수형이면 2진수의 곱도 그럭저럭 할만한데 2진수 실수의 곱이라면?

------------------------------------------------------------------------------------------

>> 2진수 실수 끼리의 곱

만약 101.11과 1.001끼리 곱한다고 가정하자

일단 101.11은 5.75이고 1.001은 1.125 이다.

그 곱은 6.46875 이다.

실수의 곱셈도 정수의 곱셈과 별 차이가 없다.

다만 2진수 라는점이 다를뿐이다.

1 0 1 . 1 1

1 . 0 0 1

-------------

1 0 1 1 1

0 0 0 0 0

0 0 0 0 0

1 0 1 1 1

-----------------

1 1 0 0 1 1 1 1

즉 110.01111이 나온다.

위의 변환법을 통해 변환해보면 6.46875가 나옴을 알수있다.

------------------------------------------------------------------------------------------

>> 2진수 끼리의 나눗셈

1100(2bit) = 12

0011(2bit) = 3

좀 조잡하지만 대충 이해하면 되겟다 -_-

그러니까 기본적으로 10진수랑 나눗셈 같다고 보면 된다.

1100이 0011보다 크니까 나눠주는데 최대한 앞에서 나눠준다.

1100/11 하면 그냥 100이다. 즉 12/3 = 4다

이제 나눗셈도 실수로 해보자

위에서 계산한 110.01111에 101 . 11을 나눠보자

1 0 0 1

------------------

1 0 1 . 1 1 / 1 1 0 . 0 1 1 1 1

1 0 1 . 1 1

------------------

0 . 1 0 1 1 1

1 0 1 1 1

------------------

0

2^5 / 2^2 = 2^3

1001 / 2^3 = 1.001

------------------------------------------------------------------------------------------

>> multiplication method : 2bit -> 2bit

원리따윈 패스

그냥 방법만 설명함

위에서 사용한 0.101을 다시 활용함

0.101 X 1010 = 110.010

> 2진수 0.101에 10을 곱해준다. 1010(2bit) = 10 이다. 왜 10을 곱하느냐? 10진수로 전환하니까 ㅇㅋ?

0.010 X 1010 = 10.100

> 여기서 나온 010은 뭐냐? 앞에서 계산한 110.010에서 소수부분만 따온것이다.

0.100 X 1010 = 101.0

> 회수는 세자리니까 세번이다.

110.010

10.100

101.0

이렇게 세가지의 수를 얻었다.

위의 division method에서는 제일 아래수만 취했지만 이방법은 약간 다르다

각 수의 정수부분이 각 소수의 자리수가 되는것이다.

위의 수들을 10진수로 변환해보면 위에서부터 6, 2, 5가 된다.

그래서 0.625가 된다. 참 쉽죠?

------------------------------------------------------------------------------------------

>> 실수형 10진수를 2진수로 변환하는법

그냥 대충 임의의 10진수 6.31을 이진수로 변환해보자

정수부분과 실수 부분을 따로 분리해서 계산한다.

정수부분을 계산해보면 110(2bit) 이다.

실수부분이 문제인데 이역시 정수 계산하듯이 하면 된다.

| 0.31

2 |-----

| 0.62 ....... 0

2 |-----

| 1.22

| 0.22 ....... 1

2 |-----

| 0.44 ........ 0

2 |-----

| 0.88 ....... 0

뭐 대충 이런식이라. 십진수를 이진수로 변환할때처럼 나눠주는게 아니라

이건 거꾸로 곱해주는것이다. 곱하다가 소수부분이 실수로 넘어가게되면

그 수를 옆으로 빼주고 다시 계속 곱해주는것이다.

문제는 가끔씩 딱 0으로 맞아떨어지는수도 있는데 대부분 순환소수가 발생한다는점이다.

그냥 알아서 잘라 먹으면 된다.

위에서부터 소수 첫째자리다 위의 경우는 0.0100이 된다.

아까 계산한 정수부분을 합쳐주면 110.0100(2bit) 가 된다.

이 수를 다시 십진수로 바꿔서 검산을 해보면 6.25가 된다.

분명히 위에서 시작은 6.31 이였다. 근데 왜 결과값은 6.25일까

그 이유는 위에 소수점 변환을 할때 귀찮아서 중간에 잘라서 그렇다.

연산을 진행하면 진행할수록 원본값이 가까워진다.

참고로 소수점 아래 54자리 수는

110.01001111010111000010100011110101110000101000111101

이다.

이걸 다 변환해보면 아마 원본값이랑 비슷하게 될것이다.

----------------------------------------------------------------------------------------------------

대충 여기까지

내가 공부하다가 나중에 햇갈릴때 참고하려고 정리한 자료

참고는 네이버 검색자료들

'IT > Legacy' 카테고리의 다른 글

한글 putty 다운로드  (0) 2013.01.11
Linux vi편집기 명령어  (0) 2013.01.11
Fedora Core 4 자바 설치하기  (0) 2013.01.11
OpenCV 설치 하기  (0) 2013.01.11
보수 표현(부호 절대값, 1의 보수, 2의 보수, excess -7)  (0) 2013.01.11

+ Recent posts