March 2010 Archives












































































































































































































































































































































































































































java.math.BigDecimal의 바른 사용에 대해서-2


또 쏘장입니다.

 

이전 글에서도 언급을 했지만 개발시 계산을 할 때 보통 primitive date type을 사용해서 계산을 하고, 아주 정확한 계산을 할 때 java.math.BigDecimal을 사용한다고 했습니다.

오늘도 정확한 계산을 위해 사용하는 BigDecimal을 잘 못 사용하고 있는 경우를 발견해서 이전글과 연결하여 포스팅 해봅니다.

 

만일 다음과 같은 값이 있다고 가정을 하고 나누기를 해봅니다.

double d1 = 1.0;

double d2 = .3;

 

d1 / d2 ==> 3.3333333333333335라는 결과가 나옵니다.(이건 제 노트북에서 나온 값이구요. 다른시스템 다른 OS에서는 다른 값이 나올 수도 있습니다.)

이상 없군요. ^^;

 

이제 동일한 값을 BigDecimal로 처리해봅시다.

BigDecimal b1 = BigDecimal.valueOf(1.0);

BigDecimal b2 = BigDecimal.valueOf(.3);

 

b1.divide(b2);

==> java.lang.ArithmeticException을 토해내는군요. (Non-terminating decimal expansion; no exact representable decimal result.)

이유는 계산에 끝이 보이지 않기 때문이라지요. 3.33333333333333333333333333333.....이 무한으로 나가는 경우입니다.

이런 경우가 자주 발생하지 않는 경우라도 한 번 발생하면 대책이 없지요. 소위 말하는 "시.한.폭.탄"이라고나 할까요.

 

오늘 발견한 잘 못 사용한 예도 바로 이 경우였습니다.

어 쩌다 한 번 나올까 말까한 로직에서 나와버린 거지요. ^^;

 

그러면 어떻게 하면 될까요.

BigDecimal을 사용해서 나누기(java.math.BigDecimal#divide())를 할 때는 반드시 소수 몇 째자리까지 계산을 할 것인지 지정을 해주어야 합니다.

 

아래와 같이 말이지요.

b1.divide(b2, MathContext.DECIMAL32); 처럼요.

MathContext에는 static으로 DECIMAL32,64,128이 선언이 되어있고 정밀도는 32-7, 64-16, 128-34로 지정이 되어있습니다. 만일 그 이상의 정확도를 요하는 경우라면 직접 new MathContext(256)처럼 정밀도를 지정해주시면 됩니다.

 

BigDecimal을 이용한 나누기 얘기가 나온김에 나누기에 관한 method도 한 번 살펴봅시다.

  • b1.divide(b2, BigDecimal.ROUND_HALF_UP);
    이경우는 수수자리는 버려진다고 보시면 됩니다. round mode는 BigDecimal의 상수를 참조하세요.
  • b1.divide(b2, RoundingMode.HALF_UP);
    RoundingMode enum에서 사용하는 상수는 BigDecimal에 정의 된 상수와 같습니다. 살펴보니. HALF_UP(BigDecimal.HALF_UP)으로 되어 있더군요.
  • b1.divide(b2, MathContext.DECIMAL32);
    정밀도를 계산결과 소수 6자리로 지정한 경우입니다. 이 경우는 3.333333이 나오겠지요.
    정밀도는 이전에 언급 된 내용을 참고하세요.
  • b1.divide(b2, new MathContext(256));
    이 경우는 정밀도를 직접 지정한 경우입니다.
  • b1.divide(b2, 16, BigDecimal.ROUND_HALF_UP);
    이 경우는 소수 16자리까지 scale을 하는 경우입니다. 소수 17자리에서 반올림됩니다.
  • b1.divide(b2, 16, RoundingMode.HALF_UP));
    위의 경우와 같습니다. 다만 rounding mode를 enum으로 사용하고 있을 뿐입니다.

이상으로 java.math.BigDecimal#divide()에 대해 살펴보았습니다.

 

뭐든지 잘 쓰면 약이 되고 잘 못 쓰면 독이됩니다.

항상 명심하시고 개발해주시길 바랍니다.

오늘도 즐거운 하루 보내시길...

이상 쏘장이었습니다.



[출처] java.math.BigDecimal 의 바른 사용에 대해서-2|작성자 아론

늘 이야기 했지만 무척 쉽다.


MTOS-4.34-en.zip

위 파일을 받아서

./tmp로 들어가 압축풀고

unzip MTOS-4.34-en.zip

블로그를 설치한 폴더 (../mt/)에 덮어 씌우면 끝.

cp -a ./MTOS-4.34-en/* ../mt/