Tech 정적코드분석/코딩표준 프로그래밍

부동소수점 비교 연산 – 그 치명적 오류

JSF AV C++ Coding Standard와 MISRA-C Coding Standard 에서는 반복문 카운터로 부동 소수점 사용을 금하고 있습니다. 반복문 카운터로 부동 소수점형 변수를 사용하면 필연적으로 부동소수점 비교 연산을 수행하게 되는데 컴퓨터는 그 태생적 한계로 정확한 부동 소수점 값을 저장 및 비교하는 것이 불가능합니다. 따라서 부동소수점을 연산에 사용하면 반올림 혹은 버림 문제가 필연적으로 발생하기 때문에 정확한 연산이 필요한 곳에서는 사용을 자제해야 합니다.

에이, 저두 그건 알아요. 누가 그렇게 쓰나요? 라고 반문하신다면, 그 실제 사례를 소개하겠습니다.

부동소수점 변수를 반복문 카운터로 사용한 예

KLDP를 뒤져서 한 가지 사례를 찾아냈습니다. 이 프로그래머는 반복문 카운터로 부동소수점 형의 변수를 사용했고, 이 때문에 1주일 동안 에러를 찾아내느라 고생했습니다.

ex

어떤 프로그래머가 for문 안의 반복문 카운터를 부동소수점 형으로 사용했었던 것입니다. 몇 가지 가정하에 위 상황을 재현하기 위해서 C++로 코드를 작성했습니다.

가정 1. for 반복문은 1000번 돌면서 작업을 수행
가정 2. 프로그래머는 루프 카운터로 작업을 수행

#include <iostream>

using namespace std;

int main()
{
 float f32 = 0.0F;
 int i = 0;
 for(f32 = 0.0; f32 < 100.0F; f32 +=0.1F)
 {
  cout<<"f32 : "<<f32<<endl;
  /* f32로 어떠한 작업 수행 */
  i++;
 }
 cout<<"Result : "<<i<<endl;
 return 0;
}

프로그래머는 i에 100이 들어갈 것이라고 기대했을 것입니다. 그러나 결과는 어떨까요?

sc

결과는 정확하게 100이 나오지 않습니다. 위 코드에서 부동소수점 비교 연산을 수행한 것은 프로그래머의 실수이며, JSF나 MISRA-C의 규칙을 위반한 대표적 사례입니다. JSF AV C++ 을 비롯한 많은 코딩 표준에서는 부동 소수점 변수를 직접적으로 비교하지 못하도록 규정하고 있습니다. 부동 소수점?자료형은 연산 과정에서 그 값이 딱 떨어지지 않기 때문입니다. 위 코드의 연산 결과를 봐도 0.1씩 997번 더하면 99.7이 아니라 99.6991이 나오는 것을 확인할 수 있습니다. 여기서 프로그래머가 true를 기대하고 if(f32 == 99.7) 이렇게 비교문을 작성하면 시스템에 따라 false 가 나올 수 있습니다.

지금 위의 결과도 Intel CPU(Intel Core2 Duo) 상에서 GCC(3.2, mingw)로 수행해서 이런 결과가 나오는 것 뿐, FPU나 Arithmetic이 다른 CPU혹은 다른 컴파일러에서 수행을 한다면 같은 결과가 나온다고 장담을 할 수 없습니다.?이런 유형의 오류는 일일이 Breakpoint 를 걸고 값을 확인하지 않는 이상 찾기도 힘듭니다. 만약 코딩 규칙을 염두에 두고 코드를 작성했다면 KLDP에 사연을 적었던 프로그래머는 디버깅하느라 일 주일을 헛되이 쓰지는 않았을 것입니다.

부동소수점 비교 방법

그렇다면 부동소수점을 꼭 비교해야 할 일이 생기면 어떻게 해야할까요? 자바의 경우는 라이브러리에서 비교 함수를 제공합니다. 이 글을 참조하시구요, C/C++의 경우 부동소수점의 정확도를 가지고 비교하는 방법을 사용합니다.

#include <math.h> //in C
#include <cmath> // in C++

#define EPSILON 0.00001  //정확도.

bool float_compare(float a, float b)
{
 return fabs(a-b) < EPSILON; // 앞서 정의한 0.00001 이하는 비교하지 않습니다.
}

Leave a Reply

Leave a Reply

Your email address will not be published. Required fields are marked *