C언어에서 실수값을 정수값으로 변환하는 방법, 이는 C언어에서 자주 듣게되는 질문중의 하나이며, 실제 프로그래밍에서 널리 사용되고 있다. 이 글에서는 실수형(float, double, long double) 타입에 저장된 값을 정수형(int, long)으로 변환하는 방법에 대하여 알아본다. 본문에서 설명하는 내용 중 일부는 구글링을 통해 발견한 Jukka Korpela 의 글(아래 링크)을 참조하였다.

http://www.cs.tut.fi/~jkorpela/round.html

그리고, 아래 링크에서 간단한 해답을 볼 수도 있다.

http://c-faq.com/fp/round.html

----------------------------------------------------------------------------------------------------------

실수값을 정수형으로 변환하기 위해 일반적으로 많이 사용하는 코드는 다음과 같다.

    (int)(x+0.5)

그러나 이 방법은 음수에 대해서는 동작하지 않는다. 또한 int 타입 대신에 long 타입을 사용하는 것이 바람직하다. 즉, 일반적으로 많이 사용하는 코드가 그리 썩 좋은 코드는 아니라는 것이다. 이 문제에 대하여 심도있게 알아보기 전에 먼저 이러한 변환에는 자름(truncating)과 반올림(rounding) 두 가지 방식의 변환이 존재한다는 것을 기억하자. 참고로, 어떠한 타입의 실수형(예를 들어, float, double, long double)을 정수형으로 변환할 것인지는 크게 중요하지 않다. 

■ 자름 변환 (truncating conversion)

자름 변환(또는 내림 변환)은 소수점 이하의 부분은 모두 무시하는 것을 의미한다. 즉, 실수 3.9는 정수 3으로 변환된다. 이 변환은 C언어에서 기본적으로 수행되는 형변환으로, 실수값(float, double, long double)을 정수형(int, long)으로 변환할 때 자동으로 수행되는 변환이다. 만약 i가 정수형이고 x는 실수형인 경우, 다음의 대입문은 이러한 변환을 발생시킨다.

    i = x

내림 변환은 다음과 같은 형변환(casting) 연산을 할 경우에도 발생한다.

    (int) x


■ 반올림 변환 (rounding conversion)

반올림 변환은 실수형 값에서 가장 가까운 정수값으로 변환하는 것을 의미한다. 예를 들어, 실수 3.9를 반올림 변환을 하면 정수 4가 된다. 반올림 변환은 사람들이 실제 생활에서 더욱 널리 사용하는 변환이다. 그러나, C언어에서는 반올림을 직접적으로 지원하는 함수나 연산자가 존재하지 않는다. (살짝 이해가 안되는 이야기이다)

0보다 큰 실수형에 대해서는 다음과 같은 간단한 방법으로 반올림 연산을 수행할 수 있다.

    (long) (x+0.5)

그러나, 위 식은 음수에 대해서는 정확하게 동작하지 않는다. 그러므로, 실수 x가 음수인 경우를 고려하여 다음과 같이 작성해야 한다.

    x >= 0 ? (long)(x+0.5) : (long)(x-0.5)

위 표현은 실수 x에 가장 가까운 정수를 표현하는 식이다.

위의 표현을 이용하여 다음과 같이 매크로(macro) 함수를 만들 수 있다.

    #define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

위와 같이 매크로를 만들어놓으면 간단하게 반올림 연산을 수행할 수 있다. 이러한 반올림 연산은 1.5를 2로 변환하고, -1.5는 -2로 변환한다.

실수형 값을 정수형 값으로 변환할 때 오버플로우(overflow)가 발생할 수 있음을 인지해야한다. int 형 대신에 long 형을 사용하는 것은 보다 큰 범위의 정수형을 지원할 수 있다. (그러나, 32비트 컴퓨팅 환경에서는 int 나 long 이나 4바이트로 표현되므로 값의 범위는 같다. 그럼에도, 실수형은 정수형보다 더 큰 범위의 수를 표현할 수 있기 때문에 long 타입을 사용하는 것을 권장한다.)

만약 효율성이 크게 중요하지 않다면, #define 문을 사용하는 대신 아래와 같은 함수를 정의함으로써 프로그램을 보다 안정적으로 만들 수 있다.

    long round(double x) {
        assert(x >= LONG_MIN-0.5);
        assert(x <= LONG_MAX+0.5);
        if (x >= 0)
            return (long) (x+0.5);
        return (long) (x-0.5);
    }


만약 효율성이 중요시된다면 아래와 같이 매크로 형태로 만들 수도 있을 것이다.

    #define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
    error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))


위 문장은 #include <limits.h> 을 필요로 하고, 이는 long 타입을 반환하는 error 함수가 있어야 정상적으로 동작할 것이다.

Posted by kkokkal
: