본문 바로가기
c언어

C언어 부동 소수점형 알아보기(float, double, long double)

by 개발자 L 2022. 11. 27.
반응형

C언어 부동 소수점형 알아보기(float, double, long double)

네 안녕하세요, 이번 포스팅에서는 c언어 부동 소수점형 자료형에 대하여 글을 써보려고 합니다.

부동 소수점형이란 유동적으로 떠다니는 소수점이 있는 자료형을 말합니다.

그리고 소수점형으로 되어있는 자료형은 과학이나 공학 분야에서 매우 중요하게 쓰이는 요소들 중에 하나입니다.

그래서 이번 시간에는 컴퓨터에서 실수를 어떤 식으로 나타내는지, 그 외에 주의를 해야 하는 사항들이 무엇인지에 대하여 알아보도록 하겠습니다.

그럼 같이 한 번 보실까요?

 

1. 컴퓨터에서 실수를 나타내는 방법

실수는 우리가 보는 소수, 다시 말해서 소수점을 가지고 있는 수입니다.

실수 범위 안에 있는 유리수와 무리수, 그리고 제곱근 등도 분수와 루트를 씌운 형태로 표현을 하기도 하지만, 그걸 변환을 할 때 소수로 표현을 하죠?

그래서 실수는 소수점이 존재하는 수라고 표현을 해도 같은 의미라고 할 수 있습니다.

그렇다면 이러한 소수점이 있는 수를 컴퓨터에서는 어떤 식으로 표현을 할까요?
일반적으로 컴퓨터에서는 정수 부분에 일정 비트를 할당을 한 후에, 나머지 소수 부분에 비트를 할당을 하는 방식을 씁니다.

그리고 이러한 방식은 정수부와 소수부가 고정이 되어있다고 하여 고정 소수점 방식이라고 합니다.

하지만 이 방법은 수를 표현하기 매우 쉽다는 장점이 있지만, 매우 큰 수를 표현하기에는 한계가 있다는 단점이 있습니다.

이를테면 과학이나 공학 등에서는 천문학적인 숫자 범위를 다루는데, 고정 소수점 방식으로는 그러한 범위의 수를 모두 커버를 할 수가 없다는 것입니다.

그래서 이러한 단점을 극복하려 고안한 방식이 바로 부동 소수점 방식입니다.

부동 소수점이란 말 그대로 부동, 떠다닌다는 뜻을 가졌습니다.

그래서 유동적으로 소수점이 움직이는 것을 말합니다.

그래서 소수점을 움직일 수 있게 되면서 제한된 비트수로 수의 정밀도를 보다 높게 표현을 할 수 있게 되었습니다.

그래서 소수점을 앞으로 당기면 작은 정수와 큰 소수부를, 반대로 뒤로 밀면 큰 정수부와 작은 소수부를 표현할 수 있습니다.

 

2. 부동 소수점 자료형

c언어에서는 실수형의 자료형을 3가지의 자료형으로 지정을 합니다.

  • float
  • double
  • long double

여기에서 float은 32비트를 할당을 하고, double과 long double은 64비트를 할당합니다.

그리고 실수형 자료형도 헤더 파일이 존재를 하는데, <float.h>라는 헤더 파일을 쓰고요,

float의 경우는 FLT_MAX와 FLT_MIN, 그리고 double형은 DBL_MAX, DBL_MIN으로 상한치와 하한치를 선언할 수 있습니다.

그리고 정수형 자료형을 받는 형식 지정자가 있는 것처럼, 실수형 자료형을 받는 형식 지정자도 존재를 하는데요,

정수형은 %d로 받았었죠?

float형의 경우는 %f, double형을 쓸 경우에는 %f를 쓰기도 하지만, 보통 %lf로 받습니다.

또한, 실수로 표현하는 것 중에서 단순히 밑만 쓰는 것이 아니라, 지수를 쓸 때가 있죠?

c언어에서도 지수를 표현하는 방식이 존재합니다.

그런데 우리가 그냥 표시를 하는 것과 같이 큰 수인 밑, 그리고 위에 붙은 작은 수인 지수로 표현을 하지 않습니다.

프로그래밍 언어에서는 그러한 방식을 지원해주지 않기 때문이죠.

그래서 우리는 e라는 형식 지정자를 이용하여 씁니다.

이를테면 1.2e10이라고 쓴다면 1.2 x 10^10, 다시 말해서 1.2 x 10의 10 제곱이 되는 겁니다.

그리고 e를 쓰기 때문에 출력 시에 받아주는 형식 지정자도 %e를 씁니다.

그리고 e를 쓸 때 대문자를 써도 되고, 소문자를 써도 무방합니다.

단지 대문자로 출력이 되는가, 소문자로 출력이 되는가가 바뀔 뿐이랍니다.

그럼 간단한 예제들을 통해서 어떤 식으로 실수형 자료형을 표현을 하는지 한 번 같이 보실까요?

#include <stdio.h>

int main()
  
{
  float x = 1.234567890123456789;
  double y = 1.234567890123456789;

  printf("float의 크기 : %d\n", sizeof(float));
  printf("double의 크기 : %d\n", sizeof(double));

  printf("x = %30.25f\n", x);
  printf("y = %30.25f\n", y);

  return 0;
}

이렇게 실수형 자료형의 표현 방법을 알아보기 위해서 한 번 간단한 코드를 작성을 해봤습니다.

그럼 결과 화면은 어떤 식으로 나왔는지 한 번 같이 볼까요?

float의 크기 : 4
double의 크기 : 8
x =    1.2345678806304932000000000
y =    1.2345678901234567000000000

이렇게 나옵니다.

일단 소수부를 보시게 되면 double형으로 받은 것이 좀 더 정확하게 나왔습니다.

그 이유는 float형의 경우는 소수점 아래 6자리까지가 표현이 가능한 범위인데 그걸 초과했기 때문에 뒤로 갈수록 이상한 값이 출력이 되고 있음을 볼 수 있고,

double형의 경우는 소수점 아래 16자리까지 표현이 가능하기 때문에 float형 보다 좀 더 정확하게 값을 뽑아냈다는 것을 볼 수 있습니다.

이렇게 되는 이유는 앞서 말씀드렸듯이 비트 수 차이 때문입니다.

반응형

 

3. 부동 소수점 상수

부동 소수점 상수는 정수형 상수처럼 수사로 된 것을 이야기하는데,

기본적으로는 소수점을 붙여서 쓰는 수, 정수부 뒤에 소수점만 붙어있는 수, 정수부는 없지만 소수점 뒤에 소수부가 존재하는 수, 양수 또는 음수가 붙은 지수부가 붙어있는 수를 상수로 쓸 수 있습니다.

그 예시를 제가 보여드리도록 하겠습니다.

1.2345 // 기본 소수 표현
2. // 소수부가 존재하지 않으나 소수점이 붙어있는 경우
.23 // 정수부가 없으나 소수점 밑으로 소수부가 존재하는 경우
2e+10 // 양의 제곱을 하는 지수
2e-10 // 음의 제곱(분수곱)을 하는 지수
4.25E4 // 대문자 지수부
0.65e-9 //정수부, 소수부, 지수부 혼합형

그리고 여기에서 주의를 해야 하는 것은 바로 두 번째의 경우인데요,

2.은 우리가 보는 정수 2가 아닙니다.

엄연히 실수형 자료형에서 쓰는 실수 상수이기 때문에 2.0의 부동 소수점형 상수로 취급이 됩니다.

그 이유는 딱 떨어지는 수인 정수 2를 가지고 연산을 할 때도 있지만, 이렇게 부동 소수점형 자료형을 가지고서 계산을 해야만 하는 경우가 생길 수 있기 때문에 프로그래밍 언어에서는 이를 철저히 분리를 하여 의미를 부여하고 있다는 것을 인지하여야 합니다.

그렇기 때문에 소수점이 없다면 정수 연산으로 컴퓨터는 인식을 하게 됩니다.

 

4. 오버 플로우와 언더 플로우

 

4 - 1. 오버 플로우

우리가 이전에 오버 플로우에 대해서는 한 번 다뤘습니다.

정수형 자료형에서 오버 플로우가 발생할 수 있다고 했는데,

실수형 자료형에서도 오버 플로우가 발생을 할 수 있습니다.

실수형 자료형에서도 각각의 자료형이 표현할 수 있는 범위가 있기 때문에 그렇습니다.

이전 포스팅에서 언급했다시피, <float.h> 헤더 파일을 #include 문장을 통하여 끌어와서 실수형 자료형의 상한치와 하한치를 지정을 할 수 있다고 했었습니다.

그 말은 다시 말하자면 실수형 자료형도 표현을 할 수 있는 범위가 있다는 뜻입니다.

그러면 일단 오버 플로우를 내는 간단한 예제 코드를 한 번 같이 보실까요?

#include <stdio.h>

int main()
  
{
  float x = 1e39; // 오버 플로우를 발생시키는 수
  
  printf("x = %e\n", x);

  return 0;
}

여기에서 x의 값을 보면 x값이 터무니없이 큰 것을 볼 수 있습니다.

10의 39 제곱이라고 한다면 거의 무한대에 근접하는 엄청나게 천문학적인 숫자입니다.

그럴 경우에는 실수형 자료형에서 오버 플로우가 나면 컴파일러는 특별한 값을 결과 화면에 출력합니다.

x = 1.#INF00e+000

이런 식으로 출력을 하는데, 여기에서 INF가 나왔다면 이것은 infinite, 다시 말해 무한대를 의미하는 문장입니다.

그래서 수가 너무 커서 연산이 불가능하다는 뜻으로 해석이 됩니다.

그렇기 때문에 이러한 문구가 출력이 되었다면 이는 오버 플로우가 발생을 한 것입니다.

 

4 - 2. 언더 플로우

언더 플로우는 오버 플로우와는 반대의 상황입니다.

부동 소수점 수가 너무 적어서 표현을 하기 힘들 때 언더 플로우가 발생을 합니다.

float형은 소수점 아래 6자리까지 표현이 가능하고, double형은 소수점 아래 15자리까지 표현이 가능합니다.

그리고 지수부는 최대가 10의 38 제곱, 최소가 10의 -38 제곱이기 때문에 이를 넘기게 된다면 오버 플로우, 이 보다 작게 내려간다면 언더 플로우가 발생을 합니다.

그래서 값이 너무 작다면 컴파일러는 가수부를 계속 조정을 해서 그 값을 맞추려고 시도를 합니다.

그래서 소수점 자리를 더 늘린다던지 하여 어떻게든 수를 표현하려 한다는 것입니다.

그러면 이를 이해하기가 쉽도록 간단한 예제 코드를 통해서 같이 알아보도록 하겠습니다.

#include <stdio.h>

int main()
  
{
  float x = 1.23456e-38;
  float y = 1.23456e-40; // 언더 플로우 발생
  float z = 1.23456e-46; // 언더 플로우 발생

  printf("x = %e\n", x);
  printf("y = %e\n", y);
  printf("z = %e\n", z);

  return 0;
}

이렇게 쓰게 되면 x는 언더 플로우가 나지 않는 최소한의 범위 안에 있기 때문에 제대로 값이 출력이 되고,

y와 z는 최소 범위를 벗어났기 때문에 언더 플로우가 발생을 합니다.

그럼 결과를 통해 같이 알아보도록 할까요?

x = 1.234560e-038
y = 1.234558e-040
z = 0.000000e+000

이런 식으로 나옵니다.

보시면 x의 값은 정확하게 표현이 되었지만,

y의 값은 최소 범위를 초과했기 때문에 컴파일러가 값을 맞추려다가 결국에는 부정확한 값을 가지게 되었고,

z의 경우에는 연산을 하기 힘들 정도로 터무니없이 작은 값이었기 때문에 연산을 포기하고 모조리 0으로 채워버렸습니다.

이런 식의 결과물이 나왔다면 언더 플로우가 일어났다고 보시면 됩니다.

 

5. 부동 소수점을 사용할 때 주의해야 할 점

일단 부동 소수점 방식 자체가 제한된 비트 수를 가지고서 실수를 표현하는 방식이기 때문에 오차가 존재합니다.

비트는 제한되어있는데 실수의 범위는 너무 방대하기 때문에 컴퓨터가 정확한 값을 도출해내기에는 너무나도 어려운 일이기 때문입니다.

그렇기 때문에 부동 소수점 방식의 출력 값은 정확하게 고정되어 있는 값이 아니라 일반적으로 사용을 해도 문제가 없을 수준의 근삿값을 출력하게 됩니다.

그리고 이러한 오차 범위는 어떤 프로그래밍 언어든 부동 소수점 기법을 사용한다면 무조건 그러고, 그 오차를 줄이기 위해서는 비트 수를 늘리면 됩니다.

그래서 보통 표현 가능한 소수점 자릿수가 많은 double형을 써서 표현을 많이 합니다.

그럼 간단한 예제 코드를 통해서 한 번 같이 살펴보도록 하겠습니다.

#include <stdio.h>

int main()
  
{
  double x;

  x = (1.0e20 + 5.0) - 1.0e20;

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

  return 0;
}

여기에서 볼 수 있듯이 10의 20 제곱을 한 큰 수이기 때문에 그냥 표현을 하기에는 무리가 있습니다.

그래서 아무리 많은 수를 표현할 수 있는 double형이라 할 지라도, 이렇게 큰 수를 유효숫자로 취급하는 것은 힘이 듭니다.

그래서 연산의 결과는 0이 나오게 되는데, 한 번 같이 보도록 하겠습니다.

0.000000

이렇게 나오게 됩니다.

앞서 말씀드렸듯이 연산을 하고 표현을 하기에는 너무 범위가 커서 생긴 결과입니다.

그렇기 때문에 절대로 어떤 값이 완벽하게 일치하기가 거의 불가능하다고 하는 겁니다.

 

여기까지 부동 소수점형에 대하여 알아보았는데요,

다음 포스팅에서는 문자형에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사합니다 ~

반응형

댓글