[C] Study: 변수와 자료형 - (4) 부동소수점형
• 실수는 123.456과 같이 소수점을 가진 수이다.
• 실수는 매우 큰 수나 매우 작은 수를 다루는 자연 과학이나 공학 분야의 프로그램을 작성할 때는 없어서는 안 될 중요한 요소이다.
• C에서는 부동소수점 방식으로 실수를 표현한다.
• 부동소수점은 소수점의 위치가 고정돼 있지 않으며, 가수와 지수를 사용하여 실수를 표현한다.
• 가수는 유효숫자, 지수는 소수점의 위치를 나타낸다.
• C에서는 float, double, long double의 3가지의ㅜ부동소수점 자료형이 있다. PC에서는 double과 long double은 같다.
• 주어진 비트 안에서 지수와 가수를 어떻게 표현하느냐에 대한 여러가지 규격이 있다. C 언어의 부동소수점음 IEEE 754 규격을 사용한다(예전 C 컴파일러 제외). 예를 들어서 float 형은 다음과 같은 IEEE 754 Single-Precision 규격을 사용한다.
• float는 32비트, double은 64비트를 사용한다. float는 약 6개의 유효 숫자, double은 약 16개의 유효 숫자를 가질 수 있다.
• 부동소수점형은 비트의 개수가 제한되어 있으므로 아주 정확하게 값을 저장하기가 어려운 경우가 있다.
참고
유효 숫자란 믿을 수 있는 의미 있는 숫자를 말한다. 예를 들어서 2,696을 십의 자리에서 반올림하면 2,700이다. 여기서 2와 7은 의미있는 숫자이다. 반면에 뒤에 붙은 00은 단순히 자릿수를 나타내는데 사용된다. 유효 숫자의 개수는 소수점의 위치와는 상관이 없다. 2,700을 2.7e3이라고 표현해도 유효 숫자는 역시 2개이다.
참고
부동소수점형은 소수점의 위치가 움직인다는 의미에서 부동소수점 수라고 한다.
부동소수점 상수
• 부동소수점 상수를 표기하는 방법에는 두 가지가 있다.
1. 소수점 표기법: 소수점을 이용하여 실수를 표현하는 방법.
2. 지수 표기법: 지수를 이용하여 실수를 표기하는 방법
• 즉 12345.6은 1,23456x10^4로 표기가 가능하고 이것을 C에서는 1.23456e4로 표기한다. 지수 부분은 E나 e를 사용하여 표시한다.
• 주의할 점: 정수라고 하더라도 2.0처럼 뒤에 소수점을 붙이면 이것은 부동소수점 상수로서 간주되어 double형이 된다.
• 지수 표기법은 주로 매우 큰 수나 작은 수를 표기하는데 유용하다.
• 부동소수점 상수는 기본적으로 double형으로 간주된다.
• 만약 double형이 아니고 float형 상수를 만들려면 상수 끝에 f나 F를 붙여주면 된다.
3.141592F
• 다음은 유효한 부동소수점 상수의 예이다.
. // 소수점만 붙여도 된다.
.28 // 정수부가 없어도 된다.
9.26E3 // 9.26x10^3
0.67e-7 // 0.67x10^-7
형식 지정자
• float형의 값을 출력하거나 입력하려면 형식 지정자로 "%d"를 사용한다.
• double형의 값을 입출력하려면 "%lf"을 사용한다. 특히 double형의 값을 입력할 때 "%lf"을 사용하지 않으면 값이 이상하게 저장된다.
double radius;
printf("반지름 값을 입력하시오.")
scanf("%lf", &radius); // 반드시 "%lf"을 사용하여야 한다.
예제 #1
다음의 예제는 유효 숫자 개념을 알아보기 위하여 부동소수점 상수를 float형 변수와 double형 변수에 1234567890.12345678901234567890을 대입하여 본다.
#include <stdio.h>
int main(void)
{
float fvalue = 1234567890.12345678901234567890;
double dvalue = 1234567890.12345678901234567890;
printf("float형 변수=%30.25f\n", fvalue);
printf("double형 변수=%30.25lf\n", dvalue);
return 0;
}
프로그램 설명 float형은 6개 정도의 유효 숫자를 가질 수 있으므로 8번째 자릿수부터는 정확한 값이 나오지 않는다. 반면에 double형은 15자리 정도의 유효 숫자를 가짐을 확인할 수 있다. 형식 지정자 % 앞에 붙은 30.25는 전체 출력 필드의 크기를 30으로 하고 소수 부분을 25자리로 출력하라는 의미이다. |
참고
부동소수점형의 한계를 알려주는 헤더파일이 존재한다. 헤더 파일 float.h에 있는 FLT_MIN과 FLT_MAX는 float로 나타낼 수 있는 가장 작은 값과 가장 큰 값을 의미한다.
비슷하게 double형에 대해서도 DBL_MIN과 DBL_MAX가 정의되어 있다.
Q float같은 실수를 int같은 정수에 넣을 경우, 어떤 일이 발생하는가?
A 컴파일러는 이러한 경우에 경고를 한다. 실수 중에서 소수점 이하는 없어지고 정수 부분만 정수 변수에 대입된다. 12.7이라는 실수를 정수 변수에 대입하면 12만 남는다.
오버플로우와 언더플로우
• 오버플로우(overflow): 변수에 대입된 수가 너무 커서 변수가 저장할 수 없는 상황을 의미한다.
• float형 변수는 약 1x10^38 이상을 넘는 수는 저장하지 못한다. 이보다 큰 값을 대입하면 오버플로우가 발생할 것이다.
• 언더플로우(underflow): 오버플로우와 반대의 상황. 부동소수점의 수가 너무 작아서 표현하기가 힘든 상황
#include <stdio.h>
int main(void)
{
float x = 1e39;
float y = 1.23456e-46;
printf("x=%e\n", x);
printf("y=%e\n", y);
return 0;
}
프로그램 설명
(line 5) 부동소수점의 경우, 오버플로우가 발생하면 컴파일러는 해당 변수에 무한대를 의미하는 특별한 값을 대입하고 printf()는 이 값을 inf라고 출력한다.
(line 8, 9) 실행 결과를 보면 변수 y의 값은 언더플로우가 일어나서 0으로 출력되었다. 서식 지정자 %e는 지수 표기법으로 출력하라는 의미이다.
부동소수점형은 부정확할 수도 있다!
• 다음의 코드를 실행해보자.
#include <stdio.h>
int main(void)
{
float value = 0.1;
printf("%.20f \n", value);
return 0;
}
• 0.1의 값이 정확하게 출력되지 않는다. 이유는 무엇일까?
• 이진법으로는 정확하게 나타낼 수 없는 값들이 있기 때문이다. 0.1도 그 중의 하나이다.
• 십진법으로 예를 들어보자. 십진법에서는 1/3을 정확하게 나타낼 수 없다.(0.3333...이 무한히 반복된다.)
• 이진법에서는 0.1이 그렇다. 십진법에서는 0.1로 정확하게 표현되지만, 이진법에서는 0.1을 정확하게 표현하는 것이 불가능하다.
• "0.000110011..."로 시작되어서 무한히 이어진다. 물론 중간에서 반올림하면 얼마든지 실용적으로는 사용이 가능하다.