개발/컴퓨터과학

FLT_MIN의 언더플로우, 왜 이미 최솟값인데도 계속 나누기가 가능할까?

센솔 2021. 3. 9. 10:23

C언어 과외 도중 실수 언더플로우에 대한 질문이 들어왔다. 질문을 살펴보기 전에, 혹시라도 이 글을 읽는 누군가를 위해 언더플로우에 대한 개념부터 먼저 간단히 정리하고 들어가려 한다.

 

언더플로우란, 메모리가 표현할 수 있는 범위보다 작은 수를 저장할 때 생기는 문제이다. 언더플로우는 두 가지 종류가 있는데, 바로 '정수 언더플로우'와 '실수 언더플로우'이다. 

 

1) 정수 언더플로우

: 지정된 자료형의 범위보다 적은 숫자를 연산하려 할때 발생

ex) char형 변수의 표현범위는 (-128~127) 인데, -128에서 -1만큼 더 감소시키면 -129가 아닌 127이 된다.

 

2) 실수 언더플로우

: 실수를 아주 큰 수로 나누었을 때, 즉 0에 한없이 가까워질 정도로 아주 작은 수가 되었을 때 발생

ex) FLT_MIN은 아주 작은 실수다. 그런데 FLT_MIN / 100000000.0f 처럼, 이미 작은 실수를 더 잘게 나눠 0에 가까워지게 하면 그 결과값은 0이 된다.

 

 

"FLT_MIN이 어떤건데?"

FLT_MIN은 float의 양수 최솟값이다. #include <float.h> 헤더파일에 정의되어 있다.
printf("%.55f", FLT_MIN); 로 그 값을 확인해보면, 0.0000000000000000000000000000000000000117549435082228751
이라는 값을 얻을 수 있다. 그 정도로 작은 숫자라는 말씀.

 


오늘의 궁금증은 바로 실수 언더플로우에 대한 것이었다. 우선 다음 문제를 보도록 하자. 

//문제 : 다음 소스 코드를 완성하여 0.000000e+00이 출력되게 만드세요.

#include <stdio.h>
#include <float.h>

int main()
{
    float num1 = FLT_MIN;

    num1 = num1 __________________;

    printf("%e\n", num1);

    return 0;
}

  위 문제에서 빈칸에 '/ 100000000.0f' 정도의 큰 수를 넣으면 0.000000e+00이 제대로 출력되었다. (언더플로우)

그런데, '/ 100.0f' 정도로 작은 수를 넣으면 0.000000e+00이 출력되지 않고 1.175493e-40 같은 이상한 값이 출력이 되었다. 언더플로우가 일어나지 않는다니 참 이상하다.

 

왜 FLT_MIN은 이미 그 자체로 제일 작은 실수이니, FLT_MIN을 어쨌건 작은 수라도 나누면 언더플로우가 일어나야 하지 않을까? 똑같이 0.000000e+00이 출력되어야 하는게 아닐까?

 

일반적으로 생각하기에, FLT_MIN은 다음 그림처럼 이해하게 된다.

 

 

비정규값이 없는 4비트 가수부(mantissa)

FLT_MIN은 너무 작은 값이라, 아래로는 더이상 나누기가 불가하다. 따라서 아주 작은 값으로 나누더라도 그 값은 언더플로우가 일어나 0이 되어야 할 것이다.. 

 

 

그러나, 비정규값이라는 개념이 적용된다면 다음 그림처럼 이해해야 한다.

비정규값이 지원되는 4비트 가수부

위 그림처럼, FLT_MIN 아래로도 더 작은 수의 연산이 가능하다. 그 이유는 '비정규값'이 FLT_MIN과 0 사이의 언더플로우 차이를 채워주기 때문이다. 그러나, 비정규값은 유효범위를 벗어나 계산하기 때문에 계산이 부정확하다는 문제점이 있다. 값이 작아지면 작아질수록 정확도를 천천히 잃어버리기 때문에, '점진적인 언더플로우' 라고도 한다.

아까 전 코드에서 printf("%e", FLT_MIN / 100.0f); 를 했을 때 0이 아닌 이상한 값이 나온 이유는 바로 이 점진적인 언더플로우, 비정규값 때문이다. 어찌어찌 계산은 되지만 정확도를 보장하지는 않는다는 점에서 FLT_MIN 미만의 연산은 유의할 필요가 있어보인다.

 

[질문의 배경이 된 문제]

 

C 언어 코딩 도장: 8.9 연습문제: 언더플로우

다음 소스 코드를 완성하여 0.000000e+00이 출력되게 만드세요. practice_real_number_under_flow.c #include #include int main() { float num1 = FLT_MIN; num1 = num1 __________________; printf("%e\n", num1); return 0; } 실행 결과 0.000000e

dojang.io

[언더플로우 개념]

 

C 언어 코딩 도장: 8.4 오버플로우와 언더플로우 알아보기

정수 자료형과 마찬가지로 실수 자료형도 오버플로우와 언더플로우가 발생할 수 있습니다. real_number_overflow_underflow.c #include #include // 실수 자료형의 양수 최솟값, 최댓값이 정의된 헤더 파일 int m

dojang.io

[사진 출처]

 

That’s Not Normal–the Performance of Odd Floats

Denormals, NaNs, and infinities round out the set of standard floating-point values, and these important values can sometimes cause performance problems. The good news is, it’s getting better, and …

randomascii.wordpress.com

[똑같은 의문을 가졌던 누군가의 Q&A]

 

C 언어 코딩 도장: 8.4 질문있습니다.

실수형 자료형에서도 오버플로우와 언더플로우가 있는건 당연하고 그 내용도 이해했으나 왜 최소값에서 나눌때 10이 아니라  #include #include // 실수 자료형의 양수 최솟값, 최댓값이 정의된 헤더

dojang.io

 

'개발 > 컴퓨터과학' 카테고리의 다른 글

최소 스패닝 트리 (MST) + 백준 1197번  (1) 2024.03.11
컴퓨터 구조 - ISA  (0) 2021.04.14