본문 바로가기
지난 학기들의 기록/어셈블리어

[어셈블리어] 정수의 음수표현과 캐리 & 오버플로우 - (carry and overflow)

by 아메리카노와떡볶이 2020. 10. 8.
728x90
정수의 음수표현과 캐리&오버플로우

어셈블리어를 공부하기에 앞서, 데이터를 표현하는 방법에 대해서 배우게 되었습니다. 여기서, 컴퓨터가 음수인 정수를 표현하는 방법은 사람이 생각하듯이 자유롭게 표현할수 있는 것이 아니라, 0과 1의 bit형식으로 수를 표현해야하기때문에 음수를 표현하는것이 쉽지 않은 작업이라는 것을 알게되었습니다. 그렇다면 컴퓨터는 어떻게 음수를 표현할까요?

 

컴퓨터가 음수를 표현하는 세가지 방법

컴퓨터는 음수를 처리하기 위해 3가지 방법을 사용할수있다고 합니다. ( 사실상 3번의 2의 보수표현을 채택했습니다.)

 

1. Sign Magnitude (부호 절댓값) 방식

먼저 Sign Magnitude (부호 – 절댓값) 방식입니다. 가장 쉽게 생각할 수 있는 방식으로, MSB를 1이고, 남은 부분에는 절댓값을 표현합니다. 예를 들면 4비트의 –1 같은 경우 부호비트MSB 1 + 001(절댓값1) , 즉 1001 로 나타낼 수 있습니다.

 

이처럼 알아보기도 쉽고 음수를 생성하기도 다른 방식보다 쉽습니다.

하지만 이 방법은 몇가지 문제점을 가지고 있습니다.

1. 0을 +0과 –0으로 구분하여 표시하기 때문에 논리적 오류가 발생합니다. 이것을 둘 다 0으로 인식하도록 해야하기때문에 회로를 구성하는데에 있어 추가적인 작업이 소요됩니다.

2. 산술연산시에 부호와 절대값을 따로 표현해서 관리하기때문에 연산을 위한 덧셈기와 뺄셈기가 각각 필요합니다.

3. 마지막으로 음수의 비교 연산을 수행할 때도 모순이 발생합니다.

 

2. One's complement (1의 보수) 방식

두번째로 보수방식중 1의보수입니다. 표현 방법은 MSB를 1로 설정하고, 남은 부분에는 1과 0을 반전한 값을 표현합니다. 부호와 절댓값을 따로 계산하지 않아도 되고, ‘Sign Magnitude’방식보다 회로가 간단해집니다.

하지만 여전히 +0과 –0이 존재하는 논리적 오류를 가지고 있습니다. 그리고 덧셈, 뺄셈연산 은 간단해졌지만 캐리가 발생했을때 추가로 연산을 해주어야하는 문제가 남아있습니다.

이때문에 캐리 가 발생했을때 LSB에 1을 더해주는 것 을 고려하여 회로를 구성해야합니다.

 

1의 보수 방식을 이용한 뺄셈 예제를 다루어보겠습니다

ex) 5-4

8비트 2진수로, 10진수 5를 표현하면 0000 0101 이고

                              4를 표현하면 0000 0100 입니다.

4를 1의 보수방식을 이용해 음수화해야하므로, MSB를 1로 설정하고 남은 부분을 1,0을 반전시킵니다

따라서 0000 0100 ==> 1111 1011 을 얻을수 있습니다.

0000 0101 ( +5)

1111 1011 ( - 4)

===============

1 0000  0000 ( =1 )

캐리가 발생했으므로 캐리를 버리고, 추가로 LSB(최하위비트에 1을 더해줍니다).

 

3. Two's complement( 2의 보수) 방식

마지막으로 2의보수 방식입니다. 표현방법은 MSB가 1이고, 남은 부분에는 1과 0을 반전한 값에 1을 더 한 값을 표현합니다. 즉 다시 말하면 1의보수를 취한다음 LSB에 1을 더해주면 됩니다.

2의 보수 방식에 서는 0이 +0과 –0으로 존재하는 논리적 오류를 해결함은 물론이고 덧셈이나 뺄셈시에 캐리가 발생했을 때 추가적인 연산이 필요가 없습니다.

따라서 연산속도가 1의 보수보다 빠르다고 할수있습니다.

이러한 장점때문에 오늘날의 컴퓨터의 대부분이 2의 보수 표현을 채택합니다.

 

2의 보수는 실제로 사용하고 있기때문에, 2의 보수 표현에 대해서는 상세하게 알 필요가 있기때문에 예시를 여러개 다루어 보겠습니다.

 

1) 16비트의 16진수 표현 ABCD가 있을때, 이것을 2진수로 변환하여라.

이때 이 2진수가 2의 보수 방식을 통해 변환된 것이라면, 양수인가 음수인가? 

정답: 음수

 

먼저 ABCD 를 2진수화하면 ABCD ==> 1010 1011 1100 1101 입니다.

1010 1011 1100 1101이 어떠한 수를 2의보수화해서 얻어낸 값이기때문에, 부호비트를 보면 됩니다

부호비트가 1이므로 이것은 음수표현이라고 할수있습니다.

 

위의 과정은 2의 보수방식 과정을 뒤집은 것인데 이해가 힘들다면 다음을 생각하면 됩니다.

10진수의 6이 있을때 4bit의 2진수로 표현하면 0110 으로 표현할수있습니다. 이 0110을 음수로 바꾸어서 결과적으로 -6을 만들고 싶다면 2의 보수방식을 사용하면 됩니다.

위에서 배웠듯이, 0110 을 1의보수 방식을 사용하여 1001 을 얻고 + 0001을 해주면 됩니다. 결과 값은 1010 입니다.

 

이때 우리는 0110 ( +6), 1010(-6) 임을 알수있는데, 1010이 바로 문제에서 말한 2의 보수화 된 2진수를 말하는 것입니다. 저러한 조건이 없었다면 1010은 10진수로 10을 표현하는 값이지만, 2의 보수화 된 값이기때문에 부호비트를 적용하여 -6 인것입니다.

 

또한 여기에서 추가적으로 알수있듯이, 1010을 다시 2의 보수화 하면 0110이 나오고, 거기서 절대값을 취하면 음수의 절댓값 또한 알아낼 수 있습니다.

 

2) 16비트의 16진수 표현 0ADD가 있을때, 이것을 2진수로 변환하여라.

이때 이 2진수가 2의 보수 방식을 통해 변환된 것이라면, 양수인가 음수인가? 

정답 : 양수

 

마찬가지로 2진수화하면, 0ADD ==> 0000 1010 1101 1101 으로 변환할수있습니다.

이 2진수 값은 2의 보수화 된 값이므로 MSB가 부호비트로 작용하고있습니다. MSB가 0이므로 이 값은 양수입니다.

마찬가지로 0000 1010 1101 1101 이 어떤 값을 가리키는지 알고싶다면 2의 보수화 한 뒤 절댓값을 추출하면 됩니다.

0000 1010 1101 1101 ==> 1111 0101 0010 0011 즉 절댓값 31377을 나타내는 것입니다. 

 

한번 맞는지 확인하기 위해 역산 해보겠습니다.

1111 0101 0010 0011 (-31377)이라는 숫자를 가지고 있다가, 이것을 31377로 만들기 위해 2의 보수화를 진행합니다.

그 결과 0000 1010 1101 1101 임을 확인할 수 있습니다. 

 

여기서 헷갈릴수 있는 부분이, 2의 보수방식이 음수를 표현하는 방법으로 쓰였기때문에 2의 보수를 취한다는 것은 음수를 만드는 방법인가? 이렇게 생각할수있지만 , 더 엄격하게 말한다면 2의 보수방식은 숫자에 -부호를 취해주는 것입니다.  따라서 2번의 답은 양수입니다.

 

4. 2의 보수 방법을 채택하는 이유

3가지의 표현방법을 제대로 이해했다면 아래의 표를 어렵지않게 이해할 수 있을것입니다.

<표1>을 보게되면 2의부호는 단 하나의 0이 존재합니다. +0과 –0이 다르게 표기되는 Sign-magnitude 와 1의 보수는 논리적 오류를 가지고 있으므로 2의 보수에 비해 고려해야할 사항이 많을 수 밖에 없습니 다. 또한 단 한번의 덧셈으로 두 수를 더할 수 있다는 장점이 있습니다. 1의 보수에서 캐리가 발생했을 때 LSB에 1을 다시 더해주어야하므로 한번의 연산을 하기 위해 두번의 연산을 해야하지만 2의 보수에서 는 캐리가 발생했을때 캐리를 무시하므로 단 한번의 덧셈으로 연산이 가능합니다. 마지막으로 표에서 볼 수 있듯이 2의 보수에서는 0이 하나로 존재하기때문에, 다른 방법보다 음수표현을 하나 더 할수있다 는 장점이 있습니다. 이러한 장점들 때문에 2의 보수방법이 더 낫다고 할 수 있습니다.

 

캐리와 오버 플로우

위의 보수연산을 진행할때에도 캐리와 오버플로우라는 개념이 등장합니다. 캐리와 오버플로우는 흔히 혼동되는 개념이기 때문에 둘의 차이를 명확하게 공부하는것이 좋습니다.

그렇다면 캐리와 오버플로우가 도대체 무엇일까요?

 

캐리와 오버플로우란?

캐리는 n비트 데이터에서 최상위비트 MSB에서 자리 넘침이나 자리 빌림이 있는 것을 말합니다.

오버플로우는 두 값의 연산 결과 , 값의 크기가 부호비트를 침범해서 발생하는 것으로 오버플로우가 발생시 부정확한 결과값이 나옵니다.

이렇게 말로만 설명해서는 제대로 이해하기가 어렵기 때문에 예시를 통해서 설명하겠습니다.

9를 1001에 양수임을 나타내는 sign bit를 추가하여 0 1001 로 표현한 것입니다. 분명히 9를 두번 더 하는 양수와 양수의 연산이므로 결과값도 양수가 나와야합니다. 하지만 부호비트가 1인 1 0010 의 값이 나왔습니다. 즉 -2가 나온 것 입니다. 이렇게 양수와 양수, 음수와 음수의 계산에서 부호비트 가 바뀌어서 값이 나오는 것을 오버플로우라고 합니다.

 

-9 또한 마찬가지 입니다. 결과값으로 -18이 나와야하지만 , 0 1110 인 양수값이 나오게 됩니다.

오버플로우가 발생하게 되면 연산의 값이 정확하지 않은 값이 나오게 되기때문에 오버플로우가 발생했는지 알아보기 위한 방법을 숙지하고 있어야합니다.

 

다음 예시를 보겠습니다.

37과 -89 의 연산을 보여주는 자료이며, 계산결과가 정확하게 나오는 것을 볼수있습니다. 즉 오버플로우가 발생하지 않은것입니다. 이것을 판단하기위해 자리올림을 계산하게 되는데 연산위에 써져있는 00100 111이 자리올림을 표시한것입니다.

이때 가장 좌측에 있는 0과  , 그리고 그 다음의 0 을 C1 ,C2라고 부르겠습니다.

오버플로우를 판단하는 방법은 C1과 C2가 같은지를 체크하는 것입니다. C1과 C2가 같으면 오버플로우는 발생하지 않은 것이며 정확한 연산 값이 나옵니다.

만약 C1과 C2가 다르다면 오버플로우가 발생한 것으로 정확하지 않은 연산 값이 나옵니다. 그리고 여기서 C1을 우리는 캐리라고 부릅니다. 

 

다음 예시는 오버플로우가 발생해서 부정확한 연산 값이 나오는 경우입니다.

 

음수와 음수의 연산인데 양수가 나왔습니다. 오버플로우가 발생해서 부정확한 값이 나온 것입니다. 여기서 자리올림을 확인하게 되면 가장 좌측의 자리올림 C1=1 ,C2=0 으로 두 값이 다른 것을 알 수 있습니다.

( C1과 C2를 캐리아웃 ,캐리인 이라고 부르기도 합니다)

 

여기까지 사실들을 통해 정리해보겠습니다

1. 오버플로우는 양수+양수, 음수+음수의 연산에서 부호비트가 침범되면서 발생한다

==> 여기서 부호 비트를 침범한다는 말은 n비트의 표현에서 연산값이 비트 표현범위를 넘어선 것을 말합니다.

 

캐리와 오버플로우 예제를 통해 연습하기

에시 1번.

8비트 2진수화 된 10진수 (15)와 8을 더하는 연산입니다. 캐리와 오버플로우가 발생했을까요?

 

가장 최상위비트인 MSB에 자리올림이 발생하지 않았고, ( 캐리 X)

C1 과 C2가 다 0임으로 부호비트를 침범하지도 않았네요. (오버플로우 X)

 

예시 2번

다음 예시입니다. 15와 -8을 계산하는 과정입니다. 

 

가장 최상위비트에서 자리올림이 발생했으므로 ,캐리가 발생했고

C1 과 C2가 둘다 1 이므로 오버플로우가 발생하지 않았습니다. 따라서 정확한 연산 값인 7이 나왔습니다.

 

 

다음은 79와 64를 더하는 과정입니다. 

 

가장 최상위비트에 자리올림이 발생하지않았으므로 캐리가 발생하지 않았고

C1=0 ,C2=1 이므로 캐리인과 캐리아웃이 다른것을 확인할수있습니다. 오버플로우가 발생하여 부정확한 값인

-113이 나왔습니다.

 

다음은 -38과 -99의 연산입니다.

캐리가 발생했고 , C1!=C2 이므로 오버플로우가 발생했습니다 . 이제 분명하게 캐리와 오버플로우를 다들 구분하실수 있으실 것입니다 !!.

728x90

댓글