Whiteship's Note

Double.MAX_VALUE는 좀 특이하군요 @_@

Java : 2009.05.19 21:07


System.out.println(Double.compare(Double.MAX_VALUE, Double.MAX_VALUE - 1.0));

이렇게 하면 무슨 값이 출력 될까요? 앞에 있는 인자가 더 크니까 양수가 나와야 합니다.
그러나 해보시면 아시겠지만, 0이 나옵니다.

난 너무 커서 1 같이 작은 수는 빼봤자 그게 그거야...

라는 건가요.. 이건 좀 @_@
왜이러는 걸까요?
정확하게 비교하려면 어떻게 해야 할까요?

한가지 찾은 방법은 longValue() 이용해서 long으로 바꾼 다음에 계산하는 겁니다.
그러나.. 불편하자나요~
top

  1. Favicon of http://blog.lckymn.com BlogIcon Kevin 2009.05.19 23:34 PERM. MOD/DEL REPLY

    기선님께서 언급하신 longValue() 를 이용한 방법으로도 정확한 계산, 비교는 힘듭니다.
    비교는 되겠지만, 말 그대로 정확한 계산에 의한 비교는 아닙니다.
    doubleToLongBits 를 사용할수도 있긴 합니다만 이것 역시 정확한 계산은 힘들죠.

    간단한 예를 통해서 왜 그런지 알아보겠습니다.
    (사실 이런 포스트 보다 긴 댓글은 제 블로그에 직접 올려야 하는데...죄송합니다... ㅡ_ㅡ;;; )

    System.out.println("double -> longbits : " + Double.doubleToLongBits(Double.MAX_VALUE));
    System.out.println("double -> longbits - 1: " + (Double.doubleToLongBits(Double.MAX_VALUE) - 1));
    System.out.println("Double.MAX_VALUE : " + Double.MAX_VALUE);
    System.out.println("double -> longbits - 1 -> double: " + Double.longBitsToDouble(Double.doubleToLongBits(Double.MAX_VALUE) - 1));

    이코드를 돌리면 결과가 이렇게 나옵니다.
    double -> longbits : 9218868437227405311
    double -> longbits - 1: 9218868437227405310
    Double.MAX_VALUE : 1.7976931348623157E308
    double -> longbits - 1 -> double: 1.7976931348623155E308
    double에서 longbits로 바꾸고 하나 빼니까 1이 줄어들었는데,
    다시 double로 바꾸니까 차이는 0.0000000000000002E308 만큼이나 생깁니다.

    그럼 double에서 longbits로 바꾸고 1을 뺀다음에
    다시 double로 바꿨다가 longbits로 재변환후 이번에는 뺀 1을 다시 더한후에 dobule로 바꿔서
    그 값이 원래 값과 같은지 보겠습니다. 정상적이면 같아야 겠죠.

    System.out.println("Double.MAX_VALUE : " + Double.MAX_VALUE);
    System.out
    .println("double -> longbits - 1 -> double -> longbits + 1 -> double: "
    + Double.longBitsToDouble(Double.doubleToLongBits((Double.longBitsToDouble(Double
    .doubleToLongBits(Double.MAX_VALUE) - 1))) + 1));
    결과는

    Double.MAX_VALUE : 1.7976931348623157E308
    double -> longbits - 1 -> double -> longbits + 1 -> double: 1.7976931348623157E308
    다행히 이건 결과가 제대로 나옵니다.

    그럼 이제 기선님께서 언급하신 longValue()를 사용해 보겠습니다.
    똑같은 과정입니다만, 사실 longValue() method가 하는 일은
    (long) 을 써서 casting을 하는것 뿐이므로 불필요한 Double object생성을 막기 위해
    그냥 casting으로 대체해서 해보겠습니다.

    System.out.println("double -> long : " + (long) Double.MAX_VALUE);
    System.out.println("double -> long - 1: " + ((long) Double.MAX_VALUE - 1));
    System.out.println("Double.MAX_VALUE : " + Double.MAX_VALUE);
    System.out.println("double -> long - 1 -> double: " + (double) ((long) Double.MAX_VALUE - 1));

    결과는 이렇습니다.
    double -> long : 9223372036854775807
    double -> long - 1: 9223372036854775806
    Double.MAX_VALUE : 1.7976931348623157E308
    double -> long - 1 -> double: 9.223372036854776E18

    다시 double 바꾸니 엉뚱한 결과가 나옵니다.
    이번엔 아까처럼 1을 뺀후에 1을 다시 더하는 걸 해보겠습니다.

    System.out.println("Double.MAX_VALUE : " + Double.MAX_VALUE);
    System.out.println("double -> long - 1 -> double -> long + 1 -> double: " + (double) ((long) ((double) ((long) Double.MAX_VALUE - 1)) + 1));

    같은 값이어야할 결과는 역시 전혀 다른 녀석이 나옵니다.
    Double.MAX_VALUE : 1.7976931348623157E308
    double -> long - 1 -> double -> long + 1 -> double: -9.223372036854776E18

    사실 float 나 double은 숫자가 커질경우 정확한 계산을 위해서 쓰면 위험합니다.
    특히 float는 작은 수에서도 문제가 있구요.

    그래서 큰액수의 돈이 왔다 갔다 하는 금융권에서 이걸 썼다가는... 덜덜덜...
    그런 경우는 BigDecimal 을 이용하셔야 합니다.
    사실 전 금융권에 있는게 아니라서 BigDecimal을 써본적이 없습니다...^^;;;
    그냥 제가 알고 있는게 그렇다는 겁니다... 더 좋은 방법이 있으면 공유 좀... :)

    말나온 김에,
    Double.MAX_VALUE로 BigDecimal 두개를 만들고, 하나에서 BigDecimal 1을 뺀후에 비교해 보겠습니다.
    (저도 이참에 사용을 경험해 볼겸...^^;;; )

    BigDecimal bigDecimal = BigDecimal.valueOf(Double.MAX_VALUE);
    BigDecimal bigDecimal2 = BigDecimal.valueOf(Double.MAX_VALUE).subtract(BigDecimal.valueOf(1));
    System.out.println("bigDecimal.compareTo(bigDecimal2): " + bigDecimal.compareTo(bigDecimal2));
    System.out.println("bigDecimal: " + bigDecimal);
    System.out.println("bigDecimal2: " + bigDecimal2);

    결과는
    bigDecimal.compareTo(bigDecimal2): 1
    bigDecimal: 1.7976931348623157E+308
    bigDecimal2: 179769313486231569999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
    비교는 당연히 bigDecimal이 1을 뺀 bigDecimal2보다 크기 때문에 1이 나오고
    둘의 값도 아주 정확하게 나옵니다. 9가 292개 인걸 확인했습니다.

    다음부터는 이런건 블로그를 통해서 하도록 하겠습니다...ㅡ_ㅡ;;;

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2009.05.19 23:45 PERM MOD/DEL

    감사합니다. @_@ 오래전에 부동소수형이 정확한 계산에는 부적절하니까 BidDecimal을 사용해야 한다는 걸 공부한 적이 이었는데 까맣게 잊어버리고 있었습니다.

    http://whiteship.me/189

    댓글은 길어도 관계 없습니다. 좋은 내용 감사합니다.

  2. Favicon of http://entworks.tistory.com BlogIcon 엔트웍스 2009.05.20 09:11 PERM. MOD/DEL REPLY

    자바 퍼즐러에서 본 것 같은데,
    1이 아니라 어느 정도 이상의 큰 숫자를 빼면 다르다고 나올 겁니다.
    여기서 시사하는 바는,
    Double.MAX_VALUE는 비교연산의 대상으로만 삼아야한다고 기억하는 것이 아닐까 하네요.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2009.05.20 09:38 신고 PERM MOD/DEL

    나누기 10000 쯤 하니까 쪼금 바뀌긴 하더라구요.

    그러나.. 댓글 주신 것처럼 비교 대상으로 만 사용하고, 실제 값을 비교할 때는 BigDecimal을 사용하는게 좋겠네요. 감사합니다~ :)

Write a comment.




: 1 : ··· : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 : 33 : ··· : 140 :