BigDecimal의 필요성

Java SE 2007/08/20 17:23 Posted by Sun

저자 JZ Ventures사의 사장 겸 대표 컨설턴트 John Zukowski

이 아티클의 영문 원본은 http://java.sun.com/mailers/techtips/co ··· html%232 
에서 볼수 있습니다.

부동 소수점 숫자 사용은 재미있는 작업이 될 수 있다. 일반적으로 양을 다룰 때 정수가 아니라면 double 형식을 사용하려고 한다. 정수인 경우 일반적으로 int 형식이면 충분하다. 값의 크기에 따라 float나 long도 효과적일 수 있다. 그러나 화폐를 다룰 경우, 이 형식들은 최악의 선택이 되는데, 정확한 값을 제공하지 않을 때도 있기 때문이다. 단지 이진수 형식에 저장 가능한 값만 제공한다. 다음은 합계를 계산하거나 할인을 반영하거나 부가가치세를 추가할 때 double을 사용하면 겪게 되는 문제를 보여주는 간단한 예제이다.

Calc 프로그램은 금액 $100.05로 시작한 다음 사용자에게 10% 할인을 적용하고 다시 5% 부가가치세를 추가한다. 부가가치세율은 다를 수 있지만, 이 예제에서는 5%를 사용한다. 그 결과를 보자면, 이 클래스는 NumberFormat 클래스를 사용하여 결과의 형식을 지정하는데, 이 결과는 통화로 표시되어야 한다.

import java.text.NumberFormat;

public class Calc {
  public static void main(String args[]) {
    double amount = 100.05;
    double discount = amount * 0.10;
    double total = amount - discount;
    double tax = total * 0.05;
    double taxedTotal = tax + total;
    NumberFormat money = NumberFormat.getCurrencyInstance();
    System.out.println("Subtotal : "+ money.format(amount));
    System.out.println("Discount : " + money.format(discount));
    System.out.println("Total : " + money.format(total));
    System.out.println("Tax : " + money.format(tax));
    System.out.println("Tax+Total: " + money.format(taxedTotal));
  }
}

모든 내부 계산에 double 형식을 사용하면 결과는 다음과 같다.

Subtotal : $100.05
Discount : $10.00
Total : $90.04
Tax : $4.50
Tax+Total: $94.55

가운데의 Total이 기대하는 값일 수 있으나, 맨 끝의 Tax+Total 값에서 끝난다. 할인은 $10.01이 되어야 $90.04의 금액을 얻는다. 해당 부가가치세를 추가하고 나면 최종 합계가 1센트 늘어나 있다. 세무 당국은 이 점을 이해하지 못할 것이다. 반올림 오류가 문제이다. 이 반올림 오류를 바탕으로 계산한 것이다. 다음은 형식이 지정되지 않은 값이다.

Subtotal : 100.05
Discount : 10.005
Total : 90.045
Tax : 4.50225
Tax+Total: 94.54725

형식이 지정되지 않은 값을 살펴보자면, 맨 처음 떠오르는 질문은 왜 90.045가 90.05 대신 90.04가 되었는가이다. (즉, 왜 10.005가 10.00으로 반올림되는가?) 이는 소위 RoundingMode에 의해 제어되는데, 이는 Java SE 1.6에 도입된 계수법으로서 이전 릴리스에서는 그러한 제어가 없었다. 통화를 위해 도입된 NumberFormat은 기본 반올림 모드가 HALF_EVEN이다. 즉, 남은 값이 등거리(equidistant)이면 짝수 쪽으로 라운딩된다. 이 계수법에 대한 Java 플랫폼 설명서에 따르면, 통계상 이 방법은 여러 차례의 계산을 거친 후 누적 오류를 최소화한다.

RoundingMode 계수법에서 사용 가능한 또 하나의 모드는 다음과 같다.

  • CEILING 항상 양수 무한대로 라운딩한다.
  • DOWN 항상 0으로 라운딩한다.
  • FLOOR 항상 음수로 라운딩한다.
  • UP 항상 0으로부터 벗어나서 라운딩한다.
  • HALF_DOWN 항상 가장 가까운 인접 수로 라운딩한다. 단, 두 인접 수 모두 등거리라면 버림한다.
  • HALF_UP 항상 가장 가까운 인접 수로 라운딩한다. 단, 두 인접 수가 모두 등거리라면 올림한다.
  • UNNECESSARY 라운딩할 필요 없이 정확한 결과를 선언한다.

이 문제의 해결 방법을 알아보기에 앞서, 약간 다른 결과를 살펴 보도록 한다. 70센트로 시작하고 할인이 없는 경우이다.

Total : $0.70
Tax : $0.03
Tax+Total: $0.74

70센트 거래의 경우, 단순히 라운딩 문제가 아니다. 형식 지정 없이 값을 보자면 다음과 같다.

Total : 0.7
Tax : 0.034999999999999996
Tax+Total: 0.735

부가가치세인 0.035는 double로 저장할 수 없다. 즉, 이진 형식에서 double로 표현할 수 없다.

BigDecimal 클래스는 float 및 double을 사용하는 부동 소수점 연산에서 생기는 문제 몇 가지에서 도움이 된다. BigDecimal 클래스는 사실상 무제한적인 정밀도로 부동 소수점 숫자를 저장한다. 이 데이터를 다루기 위해 add(value), subtract(value), multiply(value) 또는 divide(value, scale, roundingMode) 메소드를 호출한다.

BigDecimal 값을 출력하려면 setScale(scale, roundingMode)로 단위 및 라운딩 모드를 설정하거나 toString() 또는 toPlainString() 메소드를 사용한다. toString() 메소드는 과학적 표기를 사용할 수 있지만, toPlainString()은 그렇지 않다.

BigDecimal을 사용하도록 프로그램을 변환하기 전에 그 생성 방법을 확인하는 것이 중요하다. 이 클래스에는 16개의 구성자가 있다. BigDecimal의 값을 double과 같은 primitive 객체에 저장할 필요는 없으므로 String으로부터 BigDecimal 객체를 만드는 것이 가장 좋다. 이 오류를 보여 주기 위해 간단한 예제를 소개한다.

  double dd = .35;
  BigDecimal d = new BigDecimal(dd);
  System.out.println(".35 = " + d);

예상했던 출력이 아닐 것이다.

  .35 = 0.34999999999999997779553950749686919152736663818359375

그보다는 여기서 보여주는 것처럼 string "35"를 직접 사용하여 BigDecimal을 만들어야 한다.

  BigDecimal d = new BigDecimal(".35");

그 결과 다음과 같은 출력을 얻는다.

  .35 = 0.35

값을 생성한 후 숫자의 단위와 라운딩 모드를 setScale()을 사용하여 명시적으로 설정할 수 있다. Java 플랫폼의 다른 Number 하위 클래스와 마찬가지로, BigDecimal은 변경할 수 없다. 따라서 setScale()을 호출할 경우 반환 값을 "저장"해야 한다.

  d = d.setScale(2, RoundingMode.HALF_UP);

BigDecimal을 사용하여 수정된 프로그램은 다음과 같다. 계산마다 또 다른 BigDecimal을 사용하고 그 단위를 설정해야 달러 및 센트에 대한 수학 연산이 수행된다. 분할 페니를 사용하려는 경우 단위에서 3개의 소수 자리로 갈 수도 있지만, 꼭 그럴 필요는 없다.

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Calc2 {
  public static void main(String args[]) {
    BigDecimal amount = new BigDecimal("100.05");
    BigDecimal discountPercent = new BigDecimal("0.10");
    BigDecimal discount = amount.multiply(discountPercent);
    discount = discount.setScale(2, RoundingMode.HALF_UP);
    BigDecimal total = amount.subtract(discount);
    total = total.setScale(2, RoundingMode.HALF_UP);
    BigDecimal taxPercent = new BigDecimal("0.05");
    BigDecimal tax = total.multiply(taxPercent);
    tax = tax.setScale(2, RoundingMode.HALF_UP);
    BigDecimal taxedTotal = total.add(tax);
    taxedTotal = taxedTotal.setScale(2, RoundingMode.HALF_UP);
    System.out.println("Subtotal : " + amount);
    System.out.println("Discount : " + discount);
    System.out.println("Total : " + total);
    System.out.println("Tax : " + tax);
    System.out.println("Tax+Total: " + taxedTotal);
  }
}

여기서는 NumberFormat이 사용되지 않았다. 하지만 통화 기호를 보여주고 싶다면 다시 추가할 수 있다.

이제 프로그램을 실행하면 훨씬 나은 계산이 수행된다.

Subtotal : 100.05
Discount : 10.01
Total : 90.04
Tax : 4.50
Tax+Total: 94.54

BigDecimal은 이 예제에서 보여준 것보다 더 다양한 기능을 제공한다. 정수를 사용하는데 무한대의 정밀도가 필요한 경우를 위한 BigInteger 클래스도 있다. 두 클래스에 대한 Java 플랫폼 설명서에서는 단위, MathContext 클래스, 정렬 및 동등(equality)에 대한 정보를 비롯하여 이 클래스와 관련된 자세한 내용을 확인할 수 있다.

"Java SE" 카테고리의 다른 글

2007/08/20 17:23 2007/08/20 17:23

TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/425

  1. BigDecimal의 필요성

    Tracked from LIGHT SAVER  삭제

    출처 : http://www.sdnkorea.com/blog/425?cid=921145 이 글은 썬 개발자 네트워크에서 가져왔습니다. ---------------------------------------------------------------------- BigDecimal의 필요성 Java SE/중급 2007/08/20 17:23 저자 JZ Ventures사의 사장 겸 대표 컨설턴트 John Zukowski 이 아티클의 영문 원본은 http:..

    2007/09/07 13:12
  2. BigDecimal의 필요성- 화폐를 다룰 경우

    Tracked from Great H.K  삭제

    화폐를 다룰 경우, 이 형식들은 최악의 선택이 되는데, 정확한 값을 제공하지 않을 때도 있기 때문이다. 단지 이진수 형식에 저장 가능한 값만 제공한다. 다음은 합계를 계산하거나 할인을 반영하거나 부가가치세를 추가할 때 double을 사용하면 겪게 되는 문제를 보여주는 간단한 예제이다.

    2007/09/12 18:04
  3. BigDecimal의 필요성

    Tracked from ★ dingpong의 조그만 휴식 공간 ★  삭제

    BigDecimal의 필요성에 대한 강좌 입니다. 핵심적인 내용은 이것입니다. [BigDecimal 클래스는 float 및 double을 사용하는 부동 소수점 연산에서 생기는 문제 몇 가지에서 도움이 된다. BigDecimal 클래스는 사실상 무제한적인 정밀도로 부동 소수점 숫자를 저장한다. ] 출처 : 본문 중에서 그러니까 부동 소수점

    2007/09/19 23:15

댓글을 달아 주세요

  1. 장병남  수정/삭제  댓글쓰기

    좋은 정보 얻어 갑니다. 이런 기능도 있었군요.. ^^

    2007/09/04 16:11
  2. 김철민  수정/삭제  댓글쓰기

    좋은정보 얻어 갑니다.

    2007/09/04 16:31
  3. 조문영  수정/삭제  댓글쓰기

    이런 기능도 있었네요. 감사합니다.
    역시 정보는 공유해야..^^

    2007/09/04 16:46
  4. 이병윤  수정/삭제  댓글쓰기

    금액관련하여 코딩하는데 상당히 유용한 정보군요. 좋은 정보 감사합니다.

    2007/09/04 17:28
  5. 유현아  수정/삭제  댓글쓰기

    좋은정보 감사합니다.

    2007/09/04 17:58
  6. 박성진  수정/삭제  댓글쓰기

    좋은 정보 얻었네요. 감사드려요^^

    2007/09/04 19:54
  7. 고광청  수정/삭제  댓글쓰기

    감사합니다.

    2007/09/05 00:27
  8. 전창오  수정/삭제  댓글쓰기

    감사합니다.

    2007/09/05 09:13
  9. 황문수  수정/삭제  댓글쓰기

    좋은 정보네요..

    2007/09/05 11:56
  10. 김철민  수정/삭제  댓글쓰기

    좋은 정보 얻어갑니다. 감사합니다..

    2007/09/05 12:47
  11. 김원태  수정/삭제  댓글쓰기

    흔히 지나칠 수 있는 것에도 문제가 있군요... 프로그램시에 주의해야할 중요한 내용인 것 같습니다. 감사합니다....

    2007/09/06 00:15
  12. 김진수  수정/삭제  댓글쓰기

    좋은정보가 여기 다 있었네요~
    많이 활용합니다 ^^

    2007/09/06 08:53
  13. 유광국  수정/삭제  댓글쓰기

    좋은정보에 감사드립니다..무궁한 발전을 기원해요

    2007/09/06 09:38
  14. 한용현  수정/삭제  댓글쓰기

    좋은정보 감사해요...항상 고마워

    2007/09/06 09:46
  15. 서정이  수정/삭제  댓글쓰기

    좋은정보에 이벤트까지 정말 감사 감사^^

    2007/09/06 09:51
  16. 서은주  수정/삭제  댓글쓰기

    좋은정보 감사 땡큐

    2007/09/06 09:55
  17. 유필성  수정/삭제  댓글쓰기

    자바의 세상으로 우리 함께 여행해요
    좋은정보 감사드려요!!!

    2007/09/06 10:01
  18. 김태성  수정/삭제  댓글쓰기

    금액관련하여 항상 두통을 겪는데 이런 글을 보게 되어서 두통약에 드는 비용이 절감되었습니다 ^^*

    2007/09/06 10:12
  19. 한은향  수정/삭제  댓글쓰기

    앞으로도 멋진 사이트로 남길 바래요

    2007/09/06 10:17
  20. 장선주  수정/삭제  댓글쓰기

    정말 좋은정보 감사합니다... *^^*
    이곳에 가입하고 정말 실력이 나날이 늘어 가는것 같아요~~~
    모두들 화이팅~~

    2007/09/06 11:31
  21. 허준혁  수정/삭제  댓글쓰기

    음.. 평소에 할 때는 원화만 다루다 보니.. 소수점 이하는 별 생각을 안했는데.. 달러화는 확실히 문제가 되는군요.
    좋은 정보 감사합니다.

    2007/09/06 11:35
  22. 홍동철  수정/삭제  댓글쓰기

    오 가끔 계산 플그램짤때 겪는 부분들인데 좋은정보감사드립니다.

    2007/09/07 08:02
  23. 손종학  수정/삭제  댓글쓰기

    음. 의외로 이부분에서 실수하는 개발자가 많습니다.
    아마도 컴퓨터가 이진처리하는 부분을 제대로 이해하지 않거나,
    교육을 받지 않아서 그렇겠지요..
    C 도 크게 다르지 않습니다.
    큰수나, 소수를 다룰때는 type에 주의해야 합니다.

    2007/09/07 15:23
  24. 윤태호  수정/삭제  댓글쓰기

    부동소수점 처리 정말 재미있게 잘 읽었습니다.
    수치해석을 할 경우에 활용도 가능하고 정말 중요한 부분이라고 생각했습니다.
    내용 좋았습니다.

    2007/09/08 15:23
  25. 최성원  수정/삭제  댓글쓰기

    너무나 좋은 정보 감사합니다. 소수점 이하 처리 너무 유익한 정보입니다.
    많은 도움 됐어요 수고하세요~^^ 이벤트도 너무 좋아요

    2007/09/09 14:12
  26. 이상수  수정/삭제  댓글쓰기

    어렵다 ㅎㅎ
    하지만 유용한 정보라 익혀둘 필요가 있을듯~

    2007/09/10 13:57
  27. 이성규  수정/삭제  댓글쓰기

    헉.. 이런문제가 있는줄은... 좋은 정보네요..

    2007/09/12 10:58
  28. 박정숙  수정/삭제  댓글쓰기

    좋은정보 감사합니당.. ㅋㅋ

    2007/09/12 11:54
  29. 이철  수정/삭제  댓글쓰기

    많은 부분 새롭게 알고 갑니다.

    2007/09/12 17:07
  30. 최하영  수정/삭제  댓글쓰기

    쉽게 접하기 힘든 내용이라 좋습니다. 아주 광범위하게 많이 쓰이는 기술이고요.

    2007/09/13 10:38
  31. 고진구  수정/삭제  댓글쓰기

    항상 요긴한 정보로 도움을 많이 받고 있습니다. 자바 신기술 따라잡기에 썬 개발자 네트워크만큼 좋은 곳도 없나봅니다. 언제나 좋은 자료 감사합니다.

    2007/09/16 22:44
  32. 박기형  수정/삭제  댓글쓰기

    그냥 무심하게 지니칠 수 있는 부분까지 알려주셔서 감사합니다.

    2007/09/19 02:51
  33. 박정숙  수정/삭제  댓글쓰기

    좋은 정보 감사해요~

    2007/09/19 03:21
  34. 김덕경  수정/삭제  댓글쓰기

    좋은자료 많이 많이 올려주세요..감사

    2007/09/19 06:41
  35. 성덕경  수정/삭제  댓글쓰기

    좋은정보 감사합니다.

    2007/09/19 13:08
  36. 김태현  수정/삭제  댓글쓰기

    알았던 내용이지만 다시 복습하니 새롭네요.

    2007/09/19 19:20
  37. 이종민  수정/삭제  댓글쓰기

    직접적인 개발자가 아니라서 정확한 세부사항까지 이해하기는 어려웠지만 나름 좋은 지식공유가 된것 같아서 기쁘네요... 앞으로도 좋은 지식발견이 되었으면 좋겠습니다. 감사합니다...!

    2007/09/19 21:07
  38. 진유라  수정/삭제  댓글쓰기

    좋은 정보 담아 가요 ^^

    2007/09/19 22:34
  39. 김복선  수정/삭제  댓글쓰기

    좋은 정보 많이 배우고 갑니다~

    2007/09/19 23:01
  40. 최인균  수정/삭제  댓글쓰기

    좋은 정보가 된 것 같습니다. 저도 예전에 부동 소수점 연산을 하는데 제대로 된 정보가 안나와서 무지 당황했던 기억이 납니다.ㅠㅠ제가 쓴 내용은 '부동소수의 정밀도 처리' 에 대한 것인데요. http://www.dingpong.net/tt/28 링크에서 보실 수 있습니다. 이글을 보시는 분들이라면 해당 글도 한번 참고해 주시면 도움이 되시리라 생각됩니다^-^ 트랙백 해갑니다~!!

    2007/09/19 23:16
  41. 임재욱  수정/삭제  댓글쓰기

    통화나 정밀제어 관련 어플리케이션을 개발할 때
    자바 쪽에서 항상 주의해야 할 사항 중에 하나가 넘버관련 타입을 무엇으로 정하는냐죠...^^
    유용한 글입니다...^^

    2007/12/10 10:44
[로그인][오픈아이디란?]

◀ Prev 1  ... 221 222 223 224 225 226 227 228 229  ... 626  Next ▶