비트와 자장가

정적타입 언어를 위한 자료형data type 개론 본문

개발/자료

정적타입 언어를 위한 자료형data type 개론

eastriver 2020. 9. 29. 23:26
728x90

이전 글에서 말했던 것처럼,

 

프로그램 언어의 기초

모든 프로그램은 순차, 분기, 반복의 집합체로서 동작한다. 1. 순차sequence 프로그램은 줄마다 순서대로 실행된다. 사람이 물을 컵에 받아 마시는 것을 의사코드pseudo-code(프로그래밍언어의 구조를

eastriver-today.tistory.com

컴퓨터에서는 모든 것을 숫자로 나타낸다.

 

프로그래밍 언어는 컴퓨터가 아닌 사람을 위한 것인데,

사람은 숫자로 생각하지 않으므로, 모든(실용적인) 프로그래밍언어는 자료형data type을 가지고 있다.

자료형을 한 마디로 정의하자면 이진숫자를 특정 형태로 묶은 것이다.

프로그래밍 언어에서 자료형은 사용의 관점에서

항상 명시적으로 선언해야 하거나, 추론해주거나, 내부적으로만 존재하는 세 가지 경우로 나뉜다.

파이썬이나 자바스크립트와 같은 언어가 자료형이 내부적으로만 존재하는 경우이다.

 

보다 전통적인 분류방법은 아래와 같다:

    약weak타입 - 강strong타입: 형변환이 묵시적으로 일어나는지, 그렇지 않은지에 따른 분류

    정적static타입 - 동적dynamic타입: 자료형이 컴필리에이션의 시점에 결정되는지, 런타임에 결정되는지에 따른 분류

 

가장 원시primitive 자료형은

정수integer 타입과

부동소수float 타입 두 가지다.


INTEGER

기본적으로 *최상위 비트most significant bit는 부호비트로 사용한다.

음수의 표기법은 2의 보수법2's complement을 사용한다.

최상위 비트: 가장 왼쪽에 위치한 비트로서, 보통 MSB로 줄여서 쓴다.
반대로 가장 오른쪽의 최하위 비트는 least significant bit로, LSB로 줄여 쓴다.

2의 보수법two's complement이란?

 

    n비트 체계에서
    0에서부터 1씩 증가하다가,
    최상위의 부호비트가 바뀔 때,
    -{2^(n-1)}에서부터 다시 시작한다.
    그래서 부호가 있는 자료형에서 모든 비트가 0인 값은 항상 0, 모든 비트가 1인 값은 항상 -1이다.

 

    그래서 어떤 수를 음수로 만드는 법은 그 수를 부정negate 후에 1을 더한 것과 같다.

   
    4비트 체계에서 2는 0010
    2를 부정하면 1101
    1을 더하면 1110


    이러한 표기법은 adder를 사용하여 뺄셈까지도 가능하다는 장점이 있다.
    3 - 3을 3비트 체계로 표현하면
    011 + 101로
    1000이 되지만 carry된 1이 버려져 000, 즉 0이 된다.
    위에서 예시로 들은 2를 포함해 모든 수가 마찬가지다.

 

    아래의 표를 보면 오버플로우도 이해가 될 것이다.

오버플로우: 자료형과 최댓값과 자료형의 최솟값이 이어져 나타나는 현상. 예시에서는 3 + 1 의 값은 -4가, -4 - 1은 3이 된다.

십진법 3비트 체계에서 two's complement 이진 표기
0 000
1 001
2 010
3 011
-4 100
-3 101
-2 110
-1 111

 

c 언어를 기준으로 운영체제에 따라 2 혹은 4 바이트의 크기를 가진다.

정수 타입은 범위에 따라 더 세부적으로 나눠진다.

 


short, long, int8/16/32/64

정수의 길이에 따른 분류로,

기본 정수타입보다 더 큰 숫자를 사용하고 싶거나

항상 작은 숫자들만 들어가는 변수가 있을 때 메모리를 절약하기 위해 사용한다.

unsigned

부호비트가 생략된 타입으로,

부호비트까지 숫자로 사용하기 때문에 일반 정수 타입 대비 2배 큰 수를 표현할 수 있다.

변수에 양수만 들어갈 것을 확신할 때,

역시 메모리를 절약하기 위해 사용하거나,

아주 큰 수를 계산하기 위해 사용한다.

대개는 키워드를 uint로 줄여서 표기한다.

boolean

참과 거짓 두 가지 경우의 수만 표현하는 불린을 표현하기 위해선

이론적으로는 한 개의 비트만 필요하지만,

대부분의 현대 컴퓨터는 바이너리를 바이트 단위로 처리(byte addressable)하기 때문에 메모리상 1 바이트를 차지한다.

만약 boolean을 비트로 설정할 경우 추가적인 비트쉬프트 operation이 발생해 연산자원을 더 소모하게 된다. 

때문에 기껏해야 컴파일러만 신경쓸 뿐이지, 8 비트 정수 타입(int8 등)로 불린을 표현해도 메모리 관리에 전혀 차이는 없다.

c를 포함한 많은 언어에서 0은 거짓, 0이 아닌 다른 모든 수는 참으로 취급한다.

참고로, c에서 true는 1으로, false는 0으로 정의된 정수 타입의 상수다.

character

하나의 문자를 저장할 수 있는 단위로

c 기준으로 1바이트를 차지한다.

c의 문자 타입에 들어갈 수 있는 ascii 글자의 수는 2^7이기 때문에 0부터 127까지 모든 경우의 수에 다 대응가능하면서도 1비트가 남아돈다.

그렇지만 프로그램언어에 따라 이모지까지 표현 가능한 유니코드를 지원하기도 하므로 문자 타입의 크기는 다양할 수 있다.

결국은 8 비트 정수 타입이기 때문에 서로 연산이 가능하지만, 프로그램언어에 따라 이를 막아놓기도 한다.

string

character의 배열array 타입이다.

c에는 문자열string 타입이 없는데, 어떤 고급언어든 간에 내부적으로는 아래처럼 c와 같은 방식을 취해 문자열을 메모리에 할당한다:

 

주소 →   char — char — char — ... — char — NULL

 

문자열 시작주소를 담은 포인터와, 문자들, 문자의 끝을 나타내는 NULL.

이 때문에 원칙대로라면 메모리에 할당하기 이전에 입력받을 글자 수의 최대치를 알아야 하는 것이다.

입력받을 글자 수를 전혀 예상할 수 없을 때 malloc() 등의 함수를 호출하여 동적할당을 하는데,

현대 프로그래밍에서 문자열의 처리만큼 빈번한 게 없기 때문에 대부분의 언어에선 이를 자동으로 지원한다.

 

문자열의 문제라기 보다는, 메모리 접근이 빈번한 언어에서 발행하는 배열의 문제로,

배열의 끝을 명시해주지 않는 등의 경우, 접근해서는 안 되는 셀에 들어가 값을 읽거나 쓸 수 있는 에러가 발생한다.

data, binary

사진이나 영상 등 이진 데이터를 직접 다뤄야 하는 경우가 있다.

그럴 때 이진 데이터 타입을 쓰는데,

생각해보면 데이터 타입에서 할당 등의 처리 방법은 스트링과 다를 이유가 없다.

다시말해 데이터 타입은 그냥 1 바이트 타입의 배열인 것이다.

2진법binary의 시작을 나타내는 0b로 시작해 직접 대입할 수도 있지만,

16진법hexadecimal의 시작을 나타내는 0x로 시작해 직접 대입하기도 한다.

 

e.g. 51400 == 0xC8 == 0b11000100

 


FLOAT

소수를 나타내기 위한 부동소수점 타입으로

소수점을 고정하지 않고, 소수점의 위치를 따로 표시하여

아주 넓은 범위의 수를 표현할 수 있다는 장점을 가진다.

 

정수 타입처럼 (unsigned가 아닌 경우) 부호비트가 있고,

나머진는 지수부exponent 비트와 가수부fraction/mantissa비트로 나눠진다.

 

대부분의 경우 단정밀도single precision 부동소수를 표현하기 위해

가장 널리 쓰이는 IEEE754 표준을 따른다.

이 표준에선 지수부를 2의 보수법을 사용하는 대신 가중치bias 보정을 거쳐 표기한다.

 

하지만, 먼저 2의 보수법을 사용해 부동소수를 나타내 보자.

여기에서는 지수부의 최상위 비트가 다시 지수의 부호비트로 사용된다.

e.g. 3.0 == 3^(+1)이고, 1/3 == 3^(-1) 

 

예를 들어, 십진수 21.8125를 정규화된 이진수로 나타낸다고 해보자.

소수점 위의 (21.)10 = (10101)2이고,
소수점 아래 (8125)10 = (11010)2이다.

즉 (21.8125)10 = (10101.11010)2이며,
이를 정규화하면 0.1010111010×2^5이다.

지수의 5를 이진법으로 바꾸면 101이다.

따라서, 32비트 정규화된 부동소수점수로 나타낸다면
맨 앞 비트의 부호는 0(양)이고,
지수부 부호는 0(양)이며,
지수부 나머지 6개 비트는 000101,
가수부는 101011101000…이 된다.

이것을 결합하면 (0 0 000101 1010111010000000000000000)2가 된다.

Abdelwahab Kharab; Ronald B. Guenther, 『이공학도를 위한 수치해석

 

2's complement floating point representation

 

이제 이를 IEEE754 표준에 따라 풀이과정을 수정하면 아래와 같아진다:

예를 들어, 십진수 21.8125를 정규화된 이진수로 나타낸다고 해보자.

소수점 위의 (21.)10 = (10101)2이고,
소수점 아래 (8125)10 = (11010)2이다.

즉 (21.8125)10 = (10101.11010)2이며,
이를 정규화하면 1.010111010×2^4이다.
어차피 0이 아닌 모든 소수의 최상위 비트는 1이므로
IEE754에서는 정수부를 생략한다.

지수 4를 이진법으로 바꾸면 10이다.
그러나 IEE754 표준에서 지수는 부호비트 대신 가중치 보정을 거쳐 음수와 양수를 표현한다.

n비트 체계에서라면 가중치 보정 표현 범위는 -{2^(n-1)-1}에서부터 2^(n-1)이다.

IEE754에서 지수 비트는 8개이므로
시작 수인 0000 0000은 -127으로,
마지막 수인 1111 1111은 2^(n-1), 128이 된다.

일반 이진표기와 127의 오차가 존재하는 것이다.
즉, 지수 4를 표현하기 위해서는 가중치 127을 더해줘 131을 이진표기하면 된다.

따라서, 32비트 정규화된 부동소수점수로 나타낸다면
맨 앞 비트의 부호는 0(양)이고,
지수부 비트는 지수 4 + 가중치 127의 이진표기인 10000011,
가수부는 최상위 비트인 정수부 1을 생략한 01011101000…이 된다.

이것을 결합하면 (0 10000011 010111010000000000000000)2가 된다.

 

IEE754 floating point representation

 

이처럼 부동소수점 타입에서 지수부를 표기할 때

2의 보수법을 사용하지 않는 이유는

수의 대칭성이 깨져 서로 비교를 어렵게 하기 때문이다.

 

소수부가 동일할 때, 가중치 보정법에서는 원래대로 자연스럽게

지수가 클수록 무한대에 가까워지고 지수가 작을수록 0에 가까워져

부동소수점 타입끼리의 비교는 signed integer끼리의 비교와 동일하게 수행된다.

지수가 클수록 큰 수이고, 지수가 같을 땐 가수부가 클수록 큰 수이기 때문이다.


double

배정밀도 부동소수점double precision floating point 타입.

정확도를 높이기 위해 사용하는 타입으로, 32비트가 아닌 64비트를 사용한다.

부호비트, 지수부 11비트, 가수부 52비트를 사용할 뿐 근본적인 차이는 없다.


long double

4배정밀도 부동소수점quadruple precision floating point 타입으로,

부호비트, 지수부 15비트, 가수부 112비트를 사용한다. 


특수 숫자들

0 00000000 00000000000000000000000 = +0
1 00000000 00000000000000000000000 = -0
0 11111111 00000000000000000000000 = +infinity
1 11111111 00000000000000000000000 = -infinity
? 11111111 11000000000000000000000 = signaling NaN //not a number
? 11111111 10000000000000000000000 = quiet NaN

문제점

부동소수점 타입은 실수real number를 정확히 표현하지 못한다.

일례로 단정밀도 표현에서 0.1은 (00111101110011001100110011001101)2로서,

0.1000000014901161193847656256의 값을 가진다.

이런 부정확성은 연산에 여러 문제를 낳는데,

대표적으로 결합법칙과 분배법칙이 성립하지 않는다는 점을 꼽을 수 있다:

 

(a+b)+c != a+(b+c)

(a+b)c != ac+bc

일 수 있는 것이다.

 

그럼에도 넓은 범위의 수를 표현할 수 있는 효율적인 자료형이기 때문에 아주 유용하다.

정밀도가 필요한 경우 유의해서 사용해야 한다는 점만 기억해놓자.

 

 

자료형은 상황에 맞게 쓰자.

728x90
반응형
Comments