반응형

추가자료 : http://blog.naver.com/leojesus?Redirect=Log&logNo=80045980042

아이오 교육센터의 기술자료는 다양한 서적/문서를 참고해서 교육센터 자체로 만든 자료입니다.

마음대로 퍼가셔도 되지만 꼭 출처를 밝혀 주시기 바랍니다.

float type에 대한 고찰

C 에서 실수 type은 아래의 2가지로 나눌수 있습니다.

float Type : 4byte 실수를 표현 하는 타입
double Type
: 8byte 실수를 표현 하는 타입

이 중에서 float type 에 대하여 역사적 배경에 기반하여 자세히 알아 보겠습니다.

다음 C언어 소스를 컴파일 후 실행 했을 때의 예상되는 값을 머리속으로 추측 해보고 실행하여 나오는 결과 값과 어떻게 차이가 나는 지 살펴 봅시다. 실행한 결과 값이 생각한 값과 같다면 실수의 내부 구조를 정확히 이해 하고 있으므로 더 이상 읽지 않으셔도 좋습니다.

#include <stdio.h>

int main()

{

float f = 0.1f;

float sum = 0.0f;

int i;

for( i=0 ; i<100000; i++ )

sum += f;

printf("%f\n", sum );

return 0;

}


프로그램의 결과는? 예상대로 0.1 * 100000 이므로 10000 이 나와야 정상이지만 무슨 이유 때문인지 1.5정도의 오차가 있는
9998.556641 로 출력 됩니다. 실수 표현이라 정확 할 것 같았지만 전혀 그렇지 않네요. 어떠한 이유 때문인지 실수의 내부구조를 살펴 봐야 할 것 같습니다.

정수와 다르게 실수는 표현하려는 정보가 두가지입니다. 양수/음수를 제외 하고도 말이지요. 그 두가지 정보는 바로 정수부와 소수부라는 것입니다.

3.14 라는 수가 있을 때 이수는

정수부 : 3
소수부 : 0.14

로 나눌 수 있습니다. 과연 메모리 4byte에 이수를 어떻게 표현 해야 가장 효율적일 까요?

초창기 방법은

고정 소수점 방식을 사용 했습니다. 고정 소수점 방식은 상위 16bit에는 정수부를 하위 16bit에는 소수부를 표현하는 방식이었죠.
예를 들어

10.25 라는 숫자가 있을 때

1. 이 수를 이진수로 바꾼다.
10.25 => 1010.01
2. 상위 16bit에는 1010을
3. 하위 16bit에는 01을 표현한다.

이를 그림으로 보자면 다음과 같습니다.

1 0 1 0 0 1

정수부 실수부
그림 1) 고정소수점 방식

위 그림을 보면 아주 합리적으로 보입니다. 정확히 메모리의 반을 정수부에 반은 실수부에 할당 하고 있으니까요.
하지만 이런식의 표현에서는 실수가 추구하는 두가지의 중요한 목적을 간과한 것입니다.

실수를 사용하는 목적은
- 매우 큰수를 표현하기 위해
- 매우 정밀한 숫자를 표현하기 위해

위 방식을 사용했을 경우 표현 범위는 양수만을 고려한다고 해도
범위 : 0 ~ 65536
정밀도 : 1/65536
볼 수 있다. 이는 매우 큰수도 아니고 매우 정밀한 수도 아니므로 실수의 사용 목적을 어느것도 만족하지 못합니다.


이런 표현 방법은 소수점이 중앙에 고정 되어 있다 하여 고정 소수점 방식이라 부릅니다.
그렇다면 매우 크면서도 정밀한 숫자를 표한하는 효율적인 방법은 무엇일까요?
이런 고민은 지금 우리 뿐만 아니라 우리의 선인들의 고민이기도 했습니다. 이에 유명한 국제 표준기관인 IEEE에서 아주 기발하면서도 흥미로운 제안을 합니다.

그 방법은 우리가 흔히 알고 있는 지수 표기법 이라는 것입니다.
이를 컴퓨터공학에서는 부동소수점이라고 부릅니다.

여기서 의문은 부동(
不動) 이라는 뜻은 움직이지 않는다는 것인데 고정가 무슨차이가 있느냐는 것이죠.
이때 부동이라는 뜻은 "움직이지 않는다" 가 아니고
부동(
浮動) 으로 아니부 가 아니고 뜰부를 써서 "떠서 움직이다" 라는 뜻입니다. 즉 소수점이 떠서 움직인다는 것이지요.

10.25 라는 숫자가 있을 때

1. 이 수를 이진수로 바꾼다.
10.25 => 1010.01

2. 소수점을 이동한다.
1010.01 => 0.101001 * 24
이를 그림으로 표현하면

0 0 0 0 0 0 1 0 0 1 0 1 0 0 1

부호 지수부 가수부

그림2) 부동 소수점 표현 방법

위에 방식으로 표현 하면

범위 : 2-128 - 2+127
정밀도 : 1/
8388608

로 표현 가능 하니 매우 큰 숫자도 표현 가능할뿐더러 매우 정밀한 숫자도 표현 가능 해졌다는 것입니다.


하지만 완벽할 것 같은 위방법도 두가지의 문제를 안고 있습니다. 바로

문제점 1 : 1010.01 => 0.101001 * 24 이런식의 이동은 어차피 왼쪽 1의 위치가 마지막 이동부분이므로 항상 0.1 형태로 시작한다. 그러므로 0.1까지 이동하지 말고 1.0 형태로 이동 하면 소수점의 쓸데 없는 1번의 이동을 막을수 있고 이렇게 하면 1bit의 낭비를 막을수 있다.

문제점 2 : 지수부에는 양수도 들어가야 하지만 음수도 들어가야 한다. 이때 음수를 표현하기위해 정수처럼 2의 보수를 사용한다면
sign bit를 따로 둔 실수에서는 중복처리라고 볼 수 있으므로 음수를 표현하기 위해 2의 보수 대신 127 바이어스 코드를 사용하여 문제를 해결한다.

10.25 라는 숫자가 있을 때

1. 이 수를 이진수로 바꾼다.
10.25 => 1010.01

2. 소수점을 이동한다.
1010.01 => 1.01001 * 23

이를 그림으로 표현하면

0 1 0 0 0 0 0 1 0 0 1 0 0 1

부호(s) 지수부(e) 가수부(m)

그림3) IEEE 부동 소수점 표준

메모리에는 위에 그림 처럼 저장 하고 다시 CPU로 로드 할 때는 다음 수식에 따라 로드 된다.

(-1)s * 1.m * 2(e-127)

위의 방식은 C언어 뿐만 아니라 C++, java, C# 등 모든 언어에서 사용하는 실수의 표준이므로 개발자하면 반드시 알아야 할 내용입니다.

실수의 내부 구조를 알아야 하는 이유

1. 정확한 실수의 내부구조를 알아서 논리적 에러 없는 코드 구현가능
2. double로도 표현 불가능한 초 정밀도 프로그램 (예. 항공우주, 유전자, 건축 등 ) 제작시 실수 타입 설계 가능

그렇다면 처음 논제로 들어 가서 0.1을 10만번 더했을 때 부동소수점으로 표현 했는데도 불구 하고 10000이라는 결과가 나오지 않았을까요?

그렇다면 위 지식을 배경으로 0.1의 내부 구조를 분석해 봅시다.

0.1 라는 숫자가 있을 때

1. 이 수를 이진수로 바꾼다. 소수점 이하를 바꿀때는 해당 소수점 이하의 수에 2를 계속 곱하고 연산의 결과인 정수를 위에서 아래로 쓴다.
0.1
0.2
0.4
0.8
1.6
1.2
0.4
0.8
1.6
1.2
....

0.1 => 0.0001100110011....
2. 소수점을 이동한다.
0.0001100110011.... => 1.100110011... * 2-4

이를 그림으로 표현하면

0 1 1 1 1 1 0 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0

부호(s) 지수부(e) 가수부(m)

그림3) 0.1의 IEEE 부동 소수점 표준

즉 , 그림에서 알수 있듯이 0.1이 10진수에서는 유한소수이지만 2진수를 변환하게 되면 2의 배승단위가 아니므로 무한소수가 된다. 아무리 가수부를 23자리나 표현한다 해도 유효숫자가 잘려 나가기 때문에 많은 회전수를 돌리면 오차가 발생하는 것이다.

실수 프로램시 가이드 라인)

회전수가 많거나 정밀한 수 표현시 절대 float를 사용하지 말고 double을 사용하라. double도 초정밀도 프로그램에는 적절치 않으므로 초정밀도 관련 프로그램은 type을 생성하여 사용하라.

따라서 처음 프로그램의 올바른 사용예는 다음과 같다.

#include <stdio.h>

int main()

{

double f = 0.1; // 0.1 상수는 double로 해석된다.

double sum = 0.0;

int i;

for( i=0 ; i<100000; i++ )

sum += f;

printf("%lf\n", sum ); // printf 포메팅은 double인 경우 lf사용

return 0;

}

결론 : 실수는 부동 소수점을 사용하고 float(32bit: s=>1, e=>8, m=>23), double(64bit: s=>1, e=>11, m=>52)의 내부구조를 가지고 있습니다. 간단한 실수 프로그램에는 float을 사용하고 정밀한 프로그램에서는 반드시 double을 초정밀도 프로그램에서는 type을 생성하여 사용해야 합니다.

이로써 여러분은 정수와 실수를 정확하게 알게 되셨으므로 c언어의 기본인 type을 마스터 하게 되었습니다.
다음에 진행될 연산자와 제어 구조도 계속 공부 하세요...

출처 : C기본부터 고급까지 C 교육의 모든 것. 아이오 교육센터(www.ioacademy.co.kr)

반응형

+ Recent posts