컴퓨터는 어떻게 음수를 나타낼까?
얻어갈 지식
- 보수법
- MSB ( most significant bit )
"보수법"
우선 컴퓨터가 음수를 표현하는 방법을 알아보기 전에 보수법에 대해 알아보자.
보수란 보충해주는 수라는 의미다.
좀 더 자세히 말하면
R진법의 수 X가 있을 때 X를 R의 최소 제곱수가 되도록 만드는 수를 R의 보수라 고한다.
10진법의 수 3이 있을 때 3을 10의 최소 제곱수 ( 이 경우 10 )로 만드는 수는 7이므로 ( 3+7=10 ) 10진법 3의 10의 보수는 7이다.
10진법의 수 12가 있을 때 12를 10의 최소 제곱수( 이 경우 100 )로 만드는 수는 88이므로 ( 12+88=100 ) 10진법 12의 10의 보수는 88이다.
일반적으로 R진법에는 R의 보수와 R-1의 보수가 존재한다.
10진법에는 10의 보수와 9의 보수가 존재하고,
2진법에는 2의 보수와 1의 보수가 존재하는 것이다.
보수를 구하는 공식은 다음과 같다.
R진법 N자릿수 X의 R의 보수 = R의 N승 - X
R진법 N자릿수 X의 R-1의 보수 = R의 N승 - 1 - X
위 공식을 사용하면 10진법 3 자릿수 567의 9의 보수는 10^3 - 1 - 567 = 999 - 567 = 432가 된다.
( 10의 보수는 9의 보수에서 1을 더하면 되므로 이 경우 10의 보수는 433이 된다. )
이때 식 999-567 = 432를 999 = 567+432로 바꿀 수 있다.
10진수 567의 9의 보수는 567을 999로 만드는 숫자인 것이다.
다시 말해, R진수 X의 R-1의 보수는 X의 각 자리에 들어가는 숫자를 R-1로 만드는 숫자인 것이다.
8진수 123의 7의 보수를 구하는 식은 123의 각 자리에 들어가는 숫자를 모두 777로 만드는 숫자이므로 654이고,
7진수 543의 6의 보수를 구하는 식은 543의 각 자리에 들어가는 숫자를 모두 666으로 만드는 숫자이므로 123이다.
이것을 2진수에 적용하면
2진수 101의 1의 보수를 구하는 식은 101의 각 자리에 들어가는 숫자를 모두 111로 만드는 숫자이므로 010이고,
2진수 1001의 1의 보수를 구하는 식은 1001의 각 자리에 들어가는 숫자를 모두 1111로 만드는 숫자이므로 0110이다.
따라서 2진수 X의 1의 보수는 X의 모든 비트를 반전한 것과 마찬가지다.
"음수 표현"
signed 키워드를 사용하면 변수는 음수 값을 가질 수 있다. (signed, unsigned 참조)
컴퓨터는 음수를 bit를 활용하여 표현하는데.
컴퓨터가 bit를 사용하여 음수를 나타내는 방법은 부호 크기 방식, 1의 보수법을 활용한 방식, 2의 보수법을 활용한 방식 총 3가지가 있다. 각각의 방식에 대해 알아보자.
부호 크기 방식 ( signed-magnitude )
MSB( 제일 왼쪽 비트 )를 식별자로 사용하여 해당 값이 음수인지 양수인지 나타내는 방법이다.
MSB가 0이면 양수고 1이면 음수다.
8bit로 예를 들면 다음과 같다.
0000 0001 = 1
1000 0001 = -1 ( MSB를 1로 바꿈 )
이렇게 하면 MSB만 보고 해당 수가 음수인지 양수인지 직관적으로 판단할 수 있다.
다만 몇몇 단점이 존재한다.
우선 첫 번째로 +0과 -0이 존재한다. ( 0000 0000은 +0이고 1000 0000은 -0이다 )
만약 아래와 같은 코드가 있다고 할 때 0이 두 개인 경우는 한 개인 경우보다 복잡한 계산을 해야 할 것이다.
if(k >= 0) {
// ......
}
둘째로 계산을 할 때 과정이 복잡해진다.
-12+4를 하면 -8이 나와야 한다. 하지만 이것을 단순히 계산하면
1000 1100 = -12
+0000 0100 = + 4
=============
1001 0000 = -16
-8이 나오는 것이 아니라 -16이 나온다.
signed-magnitude 방식에서는 -12+4 같은 경우 단순히 더해선 안 되고 -로 묶은 뒤 빼기 연산을 하고 MSB의 값을 1로 바꿔야 한다.
0000 1100 = 12
- 0000 0100 = 4
=============
0000 1000 = 8
위의 결과인 0000 1000의 MSB를 1로 변환하면 최종 결괏값인 -8이 나온다.
1000 1000 = -8
위처럼 MSB를 양수, 음수의 식별자로 사용하는 방법은 어느 때는 그대로 덧셈을 하고 어느 때는 반대로 뺄셈을 한 뒤 MSB값을 바꾸는 등 회로 구성할 때 생각해야 할 것들이 많다. ( 게다가, 덧셈 계산과 뺄셈 계산은 서로 다른 작업이므로 각각 구현해야 하는데 자연스럽게 구성이 복잡해진다. )
장점
- 사람이 볼 때 MSB만 보고 양수, 음수를 파악할 수 있어 직관적이다.
단점
- 0을 표현하는 방법이 2가지가 있다.
- 셈을 할 때 고려해야 할 것이 많아지고 회로가 복잡해진다.
1의 보수를 활용
1의 보수든 2의 보수든 보수법을 활용하면 음수를 양수로 나타낼 수 있다.
우선 1의 보수를 활용하여 음수를 표현하는 방법이다.
10진수 1을 8비트 2진수로 나타낸 0000 0001의 1의 보수 1111 1110은 -1을 나타낸다.
10진수 3을 8비트 2진수로 나타낸 0000 0011의 1의 보수 1111 1100은 -3을 나타낸다.
1의 보수를 활용하여 -23 + 31을 계산해보자
우선 -23은 23의 1의 보수이므로
0001 0111의 1의 보수 1110 1000이 -23이다 따라서
1110 1000 = -23
+ 0001 1111 = +31
=============
1 0000 0111
10진수 0을 8비트 2진수로 나타낸 0000 0000의 1의 보수는 1111 1111 이므로 1111 1111은 0이다.
0+1=1 이므로 1111 1111 + 0000 0001 = 1 0000 0000 = 0000 0001이다 따라서,
1 0000 0111
= 0000 0111 + 0000 0001
= 0000 1000
= 8
-23+31의 결과 값인 8이 나오게 된다.
이번에는 -12+4를 계산해보자
우선 -12는 12의 1의 보수이므로
0000 1100의 보수 1111 0011이 -12이다 따라서
1111 0011
+ 0000 0100
=========
1111 0111
MSB에서 올림이 발생하지 않았으므로 +1을 하지 않아도 된다.
1111 0111은 10진수로 247이고 signed 8비트는 247을 나타낼 수 없으므로 이는 어떤 수의 1의 보수라고 봐야 한다.
1111 0111의 1의 보수는 0000 1000이고 해당 값은 10진수로 8이므로
1111 0111은 247이 아니라 -8을 나타내는 것이다.
-12+4의 결과 값인 -8이 나온 것이다.
1의 보수를 사용하면 음수를 양수로 대체할 수 있으니 뺄셈을 모두 덧셈으로 대체할 수 있게 되고 결국 뺄셈은 구현하지 않아도 되니 부호 크기 방식보다 설계가 편해지지만 아직까지 문제점이 존재한다.
첫째로 여전히 +0과 -0이 존재한다는 것이고 ( 0000 0000도 0이고 그것의 1의 보수인 1111 1111도 0이다. )
둘째로 위에서 본 것처럼 캐리의 여부를 판단한 뒤 이어서 추가 계산을 해야 한다는 것이다.
장점
- 비트만 반전시키면 음수 값을 얻을 수 있다.
- 음수를 양수로 표현하므로 뺄셈을 덧셈으로 구현할 수 있어 회로가 단순해진다.
단점
- 캐리가 발생하는 경우를 처리해줘야 한다.
- 0을 표현하는 방법이 두 가지이다.
2의 보수를 활용
2의 보수를 활용하면 0은 오직 0000 0000으로만 나타낼 수 있게 된다.
0000 0000의 2의 보수는
1 0000 0000이므로 8비트로 할 수 있는 0의 표현은 오직 1개가 된다.
그리고 2의 보수를 활용할 때는 MSB에서 올림이 발생할 경우 해당 값을 그냥 무시한다 ( 1 0000 0000 = 0이니까 )
2의 보수를 활용하여 -23 + 31을 계산해보자
-23은 23의 2의 보수이므로
0001 0111의 2의 보수 1110 1001이 -23이다 따라서
1110 1001 = -23
+ 0001 1111 = +31
=============
1 0000 1000
1 0000 1000
= 0000 1000
= 8
-23+31의 결과 값인 8이 나오게 된다.
이번에는 -12+4를 계산해보자
우선 -12는 12의 2의 보수이므로
0000 1100의 보수 1111 0100이 -12이다 따라서
1111 0100
+ 0000 0100
=========
1111 1000
1111 1000은 10진수로 248이고 signed 8비트는 248을 나타낼 수 없으므로 이는 어떤 수의 2의 보수라고 봐야 한다.
1111 1000의 2의 보수는 0000 1000이고 해당 값은 10진수로 8이므로
1111 1000은 -8을 나타낸 것이다.
-12+4의 결과 값인 -8이 나온 것이다.
2의 보수의 장점은 아래와 같다.
장점
- 1의 보수에 1을 더하면 음수 값을 얻을 수 있다.
- 덧셈만으로 뺄셈을 구현할 수 있어 회로가 단순해진다.
- 1의 보수의 단점이 모두 해결된다. ( 0이 2개인 문제, 캐리 값에 따라 셈을 추가로 해줘야 하는 문제 )
https://st-lab.tistory.com/189
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ajunhyuk&logNo=40020654783
'프로그래밍 기초' 카테고리의 다른 글
동적 배열 (0) | 2022.05.29 |
---|---|
32bit vs 64bit (0) | 2021.10.06 |
Java에서 String 생성 시 ""와 new 의 차이 (0) | 2021.09.09 |
비트 패턴 (0) | 2021.09.09 |
signed, unsigned (0) | 2021.09.08 |