About Binary Number
이번 글에서는 기초로 돌아가는 시간을 가진다. 컴퓨터에서 정수와 실수를 어떻게 다루고 있는지에 대해서 정리하려고 한다. 우리는 일상생활에서는 10진법을 사용한다. 아무래도 손가락이 10개라서 이지 않을까? 하지만 컴퓨터에서는 2진법을 사용한다. 컴퓨터는 10진법을 이해하지 못한다.
컴퓨터가 사용하는 2진법
1bit = 0, 1 = pow(2, 1) = 0 ~ 1
2bit = 00, 01, 10, 11 = pow(2,2) = 0 ~ 3
4bit = 0000, 0001, 0010, ... 1111 = pow(2,4) = 0 ~ 15
8bit = 0000 0000, .... 1111 1111 = pow(2,8) = 0 ~ 255
bit 가 8개 8bits 는 1 byte 가 된다. 8bits 의 절반인 4bit 를 nibble 이라고 부른다. 컴퓨터가 저장하는 최소단위가 byte 이다.
8 bits = 2 nibble = 1 byte 이다.
nibble 은 4bits 이다. nibble 단위가 16진수의 단위가 된다.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, a, b, c, d, e, f
1111 1111 은 10진수로 15 15 이고 16진수로 표현하자면 f f 가 된다. 0xFF 가 된다. 0xFF 는 1 byte 가 된다.
정수 2진 표현
Kotlin 을 기준으로 Int 는 4 bytes 로 32 bits 이다.
양의 정수
5028 을 2진수로 나타내면 1001110100100 이 된다. 5028을 2진수로 표현하려면 13비트가 필요하다.
2진수에서 가장 오른쪽의 비트를 가장 작은 유효 비트로 LSM(least significant bit) 가장 왼쪽의 비트를 가장 큰 유효 비트로 MSB(most significant bit) 라고 부른다.
bit 수당 표현할 수 있는 범위를 알 수 있다.
4bit 0..15
8bit 0..255
12bit 0..4,095
16bit 0..65,535
20bit 0..1,058,575
24bit 0..16,777,215
32bit 0..4,294,967,295
64bit 0..18,446,744,073,709,551,615
간단한 덧셈을 살펴보자.
1 + 5 는 아래와 같이 2진수로 표현할 수 있다.
001
+101
-----
110
덧셈은 가장 오른쪽 LSM 부터 시작한다. 가장 오른쪽 1 + 1 은 2가 되는데 2진수에서 2가 없기에 자리올림을 한다. 따라서 해당 자리는 0으로 확정하고 한 자리를 올린다. 중간 비트는 두 수 모두 0 이기에 올린 1을 확정한다. 그리고 MSB 0 + 1 은 1이다. 따라서 110 이 된다. 10진수로 표현하면 6으로 실제 10진수의 합과 동일하다.
chmod
chmod 명령어를 통해서 access permission 을 변경해본 경험이 있을 것이다. 해당 명령어의 permission 은 정수 덧셈 연산을 통해서 이루어진다.
읽기(read) 는 4 쓰기(write) 는 2 실행(execution) 1
7 = 읽기 + 쓰기 + 실행
6 = 읽기 + 쓰기
5 = 읽기 + 실행
4 = 읽기
3 = 쓰기 + 실행
2 = 쓰기
1 = 실행
0 = none
음의 정수
음의 정수를 표현하는 방법은 여러가지가 있다. 차례대로 확인해보자.
부호와 크기(sign and magnitude)
음수를 표현하기 위해서는 부호(sign)가 필요하다. 양부호(+) 음부호(-) 가 있다. 이를 표현하기 위해서 하나의 bit 가 필요하다. 가장 왼쪽 비트인 MSB 를 부호로 사용하고 나머지 비트를 값으로 사용한다. 예를들어 4bit 가 있을 때 가장 왼쪽 비트는 부호를 나타내고 나머지 3개의 비트는 0 ~ 7 을 표현하는데 사용한다. 따라서 -7 ~ +7 사이를 표현할 수 있다.
하지만 이 표현법은 두 가지 이유로 널리 쓰이지 못하고 있다.
1. 0을 표현하는 방법이 두 가지라서 비용이 낭비된다. 4bit 에서 0은 (-0)1000 혹은 (+0)0000 이 된다.
2. XOR 과 AND 연산을 통해서 덧셈 계산을 할 수 없어진다.
예를 들어, +1 과 -1 를 더하고 싶다고 하자.
0001
+1001
--------
1010
기존 덧셈 방식과 같이 계산할 경우 우리가 기대하는 0이 아닌 -2 가된다.
1의 보수(one's complement)
1의 보수 표현법도 부호와 크기 표현법과 같이 부호 비트와 나머지 비트로 나뉜다. 음수를 표현할 때 나머지 비트에 대해 모두 NOT 연산을 한다.
1의 보수 표현법도 부호와 크기 표현법과 마찬가지로 문제가 있다.
1. 0을 표현하는 방법이 여전이 두 가지 방식이다.
2. 덧셈을 XOR 과 AND 연산을 통해서 계산할 수 없다.
순환 올림(end-around-carry) 방식을 통해서 할 수 있지만 복잡하다. 순환 올림을 처리하기 위한 하드웨어를 추가해야 하기 때문에 좋은 방법이 아니다.
현대 컴퓨터에서는 부호와 크기 표현법과 1의 보수 표현법을 모두 사용하지 않는다. 이 두 방식은 추가 하드웨어 없이는 제대로 동작할 수 없고, 하드웨어 추가는 비용이 더 든다는 말을 의미한다.
2의 보수(two's complement)
추가적인 하드웨어 없이 XOR 과 AND 연산만 사용해야 한다면 어떻게 할 수 있을까? 각 비트의 NOT 을 한 후, 1을 추가하면 음수를 얻을 수 있다. 이때 MSB에서 올림이 발생하면 이 값을 버린다.
예를들어, +1 인 4bit 표현에서 0001 을 NOT 을 하게 되면 1110 이 된다. 여기서 1을 더하면 1111 이 된다. 1111이 이 -1 이된다.
실수 2진 표현
고정소수점 표현법(fixed-point)
2진수로 소수를 표현하기 위해 2진 소수점의 위치를 임의로 정하는 방법이다. 4bit 가 있다면 왼쪽 2bit 는 정수로 오른쪽 2bit 는 분수를 표현하는데 사용한다.
고정소수점은 작동은 잘 하지만, 실숫값을 표현하기 위해서 필요한 비트 수가 너무 많기 때문에 잘 사용하지 않는다. 일반적인 문제를 해결하기 위해서는 넓은 범위의 수를 다룰 수 있어야 한다.
부동소수점 표현법
해당 예제에서 단지 4bit 만 사용했지만 부동소수점 표현법의 비효율성을 여실히 보여준다.1. 비트 조합 중에 낭비되는 부분이 많다. 0을 표현하는 방법이 4가지나 된다. 1.0, 2.0, 4.0 표현하는 방법도 2가지나 된다.
2. 비트 패턴이 가능한 모든 수를 표현하지는 못한다. 지수가 커질수록 가수의 한 패턴과 다른 패턴 사이의 값 차이가 커진다. 0.5 와 0.5 를 더하면 1.0 을 얻지만 6.5 를 표현하는 비트 패턴이 없기 때문에 0.5 와 6.0 을 더할 수 없는 부작용이 생긴다.
비효율성이 있지만, 부동소수점은 실수를 표현하는 표준 방법이다. 실제로는 훨씬 더 많은 비트를 사용하여 정밀도를 높인다.
32bit
지수부는 8bits 가수부는 23bits 가수부의 sign bit 로 이루고 있다.
64bit
지수부는 11 bits 가수부는 52bits 가수부의 sign bit 로 이루고 있다.
32bit, 64bit 모두 지수부 비트에서는 별도의 sign bit 이 없는 것을 볼 수 있다. 지수부 부호는 별도의 부호 비트가 아닌 특정 비트 패턴을 사용하여 부호를 나타내게 설계했다(exponent bias). 자세한 내용은 아래 참고 섹션의 링크를 확인하면 된다.
BCD(binary-coded decimal)
해당 방법은 4비트를 사용해 10진 숫자를 하나 표현한다. 예를 들어, 12를 2진수로 표현하면 1100 이지만 BCD 로 표현하면 0001 0010 이다. 여기서 0001 은 십의 자리에 있는 1을, 0010 은 일의 자리에 있는 2를 표현한다. 10진수를 사용하는 우리에게는 해당 표현이 훨씬 더 익숙하고 편한 방식이다.
오래된 컴퓨터들은 BCD 수를 처리하는 방법을 알고 있지만 이런 시스템은 더 이상 주류에 남아 있지 않다. BCD 시스템의 인기가 줄어든 이유는 2진수를 효율적으로 활용하지 못하기 때문이다. 일반적인 방법보다 비트를 더 많이 사용해야된다.
댓글
댓글 쓰기