이쁜왕자 만쉐~~

C언어 잡담.. 0.1을 10번 더하면 1.0 과 다르다?? 본문

낙서장

C언어 잡담.. 0.1을 10번 더하면 1.0 과 다르다??

이쁜왕자 2011. 12. 22. 17:42
0.1 을 10번 더하면 당연히 1.0 이어야 하겠지만, 컴퓨터에서는 그렇지가 않다. 그건 아주 간단한 코딩으로 확인 해 볼수 있다.

#include <stdio.h>

int main()
{
    double a = 0.0;
    int i;

    for (i=0;i<
 10 ;i++)
        a += 0.1;

    if (a == 1.0)
        printf("a == 1.0 \n");
    else
        printf("a != 1.0 !!!!\n");

    return 0;
}

 
이 간단한 코드의 결과는 언제나  a != 1.0 !!!! 를 출력하며, C 언어가 아니더라도, 실수(real number)를 처리하는 대부분의 컴퓨터 언어에서는 동일한 결과를 보인다. 

그 이유를 이해하기 위해서는 double (또는 float) 라는 데이터 타입에 대해서 알아야 한다. TCPL 에 의하면 이 데이터 타입은 달랑 이렇게 정의되어 있다.

float : single precision floating point  (단정도 부동 소수점)
double : double precision floating point 
(배정도 부동 소수점) 
long double : extra precision floating point 
(??) 

실제로 C 언어에는 저걸 어떻게 구현해야 하는가에 대한 내용이 없다. int 같은 다른 데이터 타입과 마찬가지로, 실제로 사용하는 컴퓨터나 OS 나 컴파일러에 따라 적당히 맞추어 사용하게끔 되어 있다.

하지만, 다행히도 이에 관련된 IEEE-754 라는 표준이 있어서, 대부분의 컴퓨터들은 이를 따르고 있다. 원래 표준은 1985년에 제정되었는데 ( http://en.wikipedia.org/wiki/IEEE_754-1985 ) , 2008년에 개정되어 http://en.wikipedia.org/wiki/IEEE_754-2008 로 바뀌었다.

여튼 double 은 64비트라는 유한한 자리수내에서 표현되기에, 모든 수를 완벽하게 표시하는 것은 불가능하며, 표현할 수 있는 가장 가까운 값을 사용하게끔 되어 있다. 물론 에러 없이 완벽하게 표현되는 수도 있지만, 그렇지 않는 수가 훨씬더 많다. 특히 이진수로 표현했을때, 순환소수가 되는 수는 더욱더 그렇다.

예를 들어 십진수로 0.5 는 이진수로 표현했을때 0.1 (2) 가 되어 유한소수가 되기 때문에, 정확히 표현히 된다. 하지만, 십진수로 0.1 은 이진수로 표현하면 순환소수가 되며, 이는 64비트라는 유한한 방법으로 표현할 수가 없고, 적당한 수준에서 잘라낸 근사값을 사용할 수 밖에 없다.

0.1 (10) => 0.0 0011 0011 0011 0011 .... (2)

실제로 0.1 을 double 로 저장하면 16진수로 보면 3FB9 9999 9999 999A 이라는 값이 된다. 이를 IEEE-754 표준을 기준으로 해서 다시 풀어서 환산해 보면, 0.100000000000000005551115123126 이 되어, 0.1 보다 아주 조금이나마 다른 값이 된다. 

참고로 0.1 은 살짝 커진값으로 저장되지만, 0.3 같은 경우는 3FD3 3333 3333 3333 으로 저장되는데, 계산해 보면 0.299999999999999988897769753748 이란 값이 되어 아주 조금 작아 진다.

즉, 0.1 을 3번 더한 값은 0.3 과 다르고, 0.1 을 10번 더한 값은 1.0 과 다르다.
특이한건 0.1 을 5번 더하면 0.5 와 동일하다. 그런데, 10번 더하면 1.0 과 다르다. 


(추가사항)

이런 이유로 일부 개념 없이 만들어진 계산기에서는 그 결과가 0.3 이 되어야 하나, 0.2999999999 로 출력되는 경우도 있다. 이는 위의 경우를 보면 알수 있듯이, 내부적으로는 0.299999999999999988897769753748 이라고 저장된 값을 적당한 자리수에서 '그냥 잘라 내서' 출력했기 때문에 발생한다.

이런 문제를 해결하는 것은 의외로 간단하다. '반올림' 처리를 하면 된다. 예를 들어 소수점 아래 10자리까지 출력한다고 하면, 11번째 자리에서 반올림을 하면 된다. 그냥 0.00000000005 를 더한 뒤 10자리에서 잘라 버리는 것으로 해결할 수 있다.

(좀더 정확한 계산을 위해서는 IEEE-754 표준을 기반으로 fraction 에 1 을 더해주면 된다.)

예를 들어 0.1 은 
0.100000000000000005551115123126 + 0.00000000005 = 0.100000000050000005551115123126 가 되며, 소수점 10자리까지만 출력하면 0.1000000000 이고, 소수점 아래 불필요한 0 을 제거하면 0.1 이 된다.

그리고, 문제가 되는 0.3 의 경우는
0.299999999999999988897769753748 + 0.00000000005 = 0.300000000049999988897769753748 가 되며, 소수점 10자리까지만 출력하면 0.3000000000 이고, 소수점 아래 불필요한 0 을 제거하면 0.3 이 된다.

이런 문제가 발생하는 계산기는, 초보 프로그래머가 정말 개념없이 만들었다는 것을 의미한다.

- 이쁜왕자 -
- Valken the SEXy THief~~ ^_* -


728x90
반응형
Comments