[기초] 부동소수점의 한계, 2진 소수점 표현의 한계

program/program_basic 2012. 10. 5. 10:47
반응형

Q. 부동소수점의 표현의 한계에 대해 알아보자.



1. double 형 0.333


1
2
double dbl = 0.333;
System.out.println( dbl );



위 소스의 값은 0.333이 나온다. 그러므로 다른 사람들은 이것에 이의를 제기 하지 않았을 것이다.


하지만, 2진 변환 과정에서 위 수치 계산은 무한히 반복된다.

IEEE754 배정도 실수 유효 비트수인 52비트까지를 표현하면 아래와 같다,


0011111111010101010011111101111100111011011001000101101000011101 (2진수)


이 계산은 무한히 반복됨에도 불구하고 double 형이

표현할수 있는 비트 수의 제한이 있으므로 정확한 값의 표현이 불가능하다.

위의 숫자를 소수 60자리로 하여 10진수로 표현하면 아래와 같다.


0.333000000000000018207657603852567262947559356689453125000000 (10진수)


실제로 입력은 0.333이지만

시스템 내부에서 갖고 있는 근사치는 0.3330000000000000182.... 과 같다.


하지만 System.out.println( dbl ); 의 값은 0.333 이 나온다?


그것은 System.out.println( double ); 을 따라가 보면

sun.misc.FloatingDecimal.getChars 이 메소드가 포메팅을 수행하여

보기좋은 숫자 (내부적으로는 0.333의 가장 근사치인) 0.333 이라는 결과를 나타낸다.






2. double형 4.35 * 0.9 = 값은?


윈도우 계산기로 두드려본 결과 3.915 가 나온다.

프로그램도 같을까 ?


1
2
3
4
double a = 0.9;
double b = 4.35;
 
System.out.println( a * b );


위의 결과값은 3.9149999999999996 이다.

궁금하면 이클립스 실행 ㄱㄱ


이는 3.915의 가장 가까운 근사치이다.


우리 회사에서 제공하는 Round 함수는 (java JDK 6.x 기반)


Round(수식, 반올림자릿수)


Round(1/3, 3) 의 연산이 0.333 이 나와야 하는 경우가 있다.

하지만 double 형을 채택하고 있는 위 함수는 0.333을 표현할 수 없다.



그래서 !

Round_Fuzz 라는 임의값을 더하여 보정을 하고 있다.

Round_Fuzz 의 임의값을 정해야 하는데,

Round_Fuzz 값이 너무 낮으면, 

   3.9149999999999996

   0.0000000000000001 을 더하게 되면 (Round_Fuzz)

   3.9149999999999997 이 되서 아무런 변화가 없게 되고


Round_Fuzz 값이 너무 높으면, 당연히 오차 범위가 커지게 된다.


그래서 아래와 같은 소스를 돌려본 결과


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
double n = 10000.285;
Random rand = new Random( System.currentTimeMillis() );
 
for( int i = 0; i < 1000; i++ )
{
        double op1 = rand.nextDouble() * (double)( i % 10 + 1 );
        double op2 = 1.0 / op1;
 
        if( i % 2 == 0 )
                n = n * op1 * op2;
        else
                n = Math.pow( Math.pow( n, op1 ), op2 );
         
        System.out.println( new BigDecimal( n ) );
         
        Math.round( n );
}



통계적으로 그나마 0.00000000001 이 적합하다고 계산된다.





3. 결론


컴퓨터라고 해서 초 정밀한 결과를 내주는것은 아니며,

double 형의 제길슨한 2진 표현 방식은 너무도 어렵기 때문에,

한참을 들여다 보지 않는 이상 "나는" 알수가 없었고,


double 은 double 의 곱의 결과가 어쩌면 약간의 오차가 나올수 있다.


컴터는 어렵다 (ㅠ_ㅠ)

반응형