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은 다음 그림처럼 이해하게 된다.
FLT_MIN은 너무 작은 값이라, 아래로는 더이상 나누기가 불가하다. 따라서 아주 작은 값으로 나누더라도 그 값은 언더플로우가 일어나 0이 되어야 할 것이다..
그러나, 비정규값이라는 개념이 적용된다면 다음 그림처럼 이해해야 한다.
위 그림처럼, FLT_MIN 아래로도 더 작은 수의 연산이 가능하다. 그 이유는 '비정규값'이 FLT_MIN과 0 사이의 언더플로우 차이를 채워주기 때문이다. 그러나, 비정규값은 유효범위를 벗어나 계산하기 때문에 계산이 부정확하다는 문제점이 있다. 값이 작아지면 작아질수록 정확도를 천천히 잃어버리기 때문에, '점진적인 언더플로우' 라고도 한다.
아까 전 코드에서 printf("%e", FLT_MIN / 100.0f); 를 했을 때 0이 아닌 이상한 값이 나온 이유는 바로 이 점진적인 언더플로우, 비정규값 때문이다. 어찌어찌 계산은 되지만 정확도를 보장하지는 않는다는 점에서 FLT_MIN 미만의 연산은 유의할 필요가 있어보인다.
[질문의 배경이 된 문제]
[언더플로우 개념]
[사진 출처]
[똑같은 의문을 가졌던 누군가의 Q&A]
'개발 > 컴퓨터과학' 카테고리의 다른 글
최소 스패닝 트리 (MST) + 백준 1197번 (1) | 2024.03.11 |
---|---|
컴퓨터 구조 - ISA (0) | 2021.04.14 |