Objective-C : float 타입을 사용할 때 주의할 점

float 타입의 소수의 정확성 문제

float은 Objective-C에서 가장 흔히 쓰이는 타입 중 하나이다. 이는 범위가 그리 크지 않은 실수를 다룰 때 사용하는데, 어처구니 없이 예상을 빗나가는 결과를 종종 만들어 내기도 한다. 다음 예를 보자.

#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
    @autorelease {
        int count = 0;
        for (float i = 10; i != 0; i -= 0.1) {
            count ++
            printf("count = %d\n", count);
        }
    }

    return 0;
}

이 코드의 루프는 i가 10에서 0.1 씩 감소하여 0이 되기 전까지 돌게되므로 100번 돌 것이라고 예상한다. 하지만 실질적으로 이는 무한루프이다!!! 왜냐구? 0.1 – 0.1 은 0이 아니라 아마도 근소하게 더 작은 -0.00001 쯤이 될 것이기 때문이다. 따라서 이 코드는 i != 0 이 아닌 i >= 0을 써야 안전하다. 이러한 증상이 일어나는 경우를 살펴보기 위해서는 다음 코드를 보도록 하자.

int main (int argc, const char *argv[]) {
    float f = 1234.123456789;
    printf("%f\n",f);
    printf("%.9f\n",f);
    return 0;
}

코드를 돌려보면

1234.12343
1234.12345086

이라는 황당한 결과를 볼 수 있다. 첫번째 값을 보면 중간부분이 짤리는데 이는 float 타입에 넣을 수 있는 것보다 더 많은 비트를 대입했기 때문이다. 하지만 5678 부분이 3으로 반올림(?)되어 들어갔다. 두 번째 예에서는 소수점 이하 9자리를 표시하고자했지만, 그 정도 디테일에 대한 정보는 애초에 없었으므로 아무 값이나 들어가게 된다. 즉 float 타입은 아주 작은 값을 다룰 때 문제가 발생하게 된다. 이런 오류는 예측이 힘들고 버그로 발현되면 디버깅이 매우 어려우므로 float 타입으로 실수를 비교할 때는 주의해야 한다.