더블 대 BigDecimal?


답변:


446

A BigDecimal는 숫자를 나타내는 정확한 방법입니다. A Double는 특정 정밀도를 가지고 있습니다. 다양한 크기의 두 배 (예 : d1=1000.0d2=0.001) 0.001로 작업하면 크기 차이가 너무 클 때 합산 할 때 모두 삭제 될 수 있습니다 . 이런 BigDecimal일은 일어나지 않을 것입니다.

의 단점 BigDecimal은 느린하다는이며 (인해 프로그램 알고리즘 그렇게 약간 더 어렵다 + - */하지의 과부하).

돈을 다루고 있거나 정밀도가 필수 인 경우을 사용하십시오 BigDecimal. 그렇지 않으면 Doubles충분히 좋은 경향이 있습니다.

그들이 내가 여기보다 더 잘 설명하는 것처럼 javadoc 을 읽는 것이 좋습니다. BigDecimal:)


그래, 나는 주식의 가격을 계산하고 있으므로 BigDecimal 이이 경우 유용하다고 생각합니다.
Truong Ha

5
@Truong Ha : 가격으로 작업 할 때 BigDecimal을 사용하려고합니다. 데이터베이스에 저장하면 비슷한 것을 원합니다.
extraneon

98
"BigDecimal은 숫자를 나타내는 정확한 방법"이라고 말하는 것은 오해의 소지가 있습니다. 1/3 및 1/7은 10 진수 시스템 (BigDecimal) 또는 2 진수 시스템 (float 또는 double)에서 정확하게 표현할 수 없습니다. 1/3은 3, 6, 9, 12, 12 등으로 정확하게 표현 될 수 있고 1/7은 7, 14, 21 등으로 정확하게 표현 될 수 있습니다. BigDecimal의 장점은 임의의 정밀도입니다 그리고 인간은 기초 10에서 발생하는 반올림 오류에 익숙합니다.
procrastinate_later

3
Netflix 리본로드 밸런서 코드가 두 배를 처리하는 이유를 이해하는 데 도움이됩니다.if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
michaelok

@extraneon " 정확도 가 필수 인 경우 BigDecimal"를 사용 하면 Double이 더 많은 "정밀도"(더 많은 숫자)를 갖게됩니다.
jspinella

164

영어 실력이 좋지 않으므로 여기서 간단한 예제를 작성하겠습니다.

    double a = 0.02;
    double b = 0.03;
    double c = b - a;
    System.out.println(c);

    BigDecimal _a = new BigDecimal("0.02");
    BigDecimal _b = new BigDecimal("0.03");
    BigDecimal _c = _b.subtract(_a);
    System.out.println(_c);

프로그램 출력 :

0.009999999999999998
0.01

누군가 아직도 더블을 사용하고 싶어? ;)


11
@eldjon 사실이 아닙니다.이 예를보십시오. BigDecimal two = new BigDecimal ( "2"); BigDecimal 여덟 = 새로운 BigDecimal ( "8"); System.out.println (two.divide (eight)); 0.25를 인쇄합니다.
Ludvig W

4
복식 : D
vach

그럼에도 불구하고 플로트를 대신 사용하는 경우에는 BigDecimal과 동일한 정밀도를 얻지 만 더 나은 성능을 얻을 수 있습니다.
EliuX

3
@EliuX Float는 0.03-0.02에서 작동하지만 다른 값은 여전히 ​​정확하지 않습니다 : System.out.println(0.003f - 0.002f);BigDecimal은 정확합니다 :System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Martin

50

double과는 크게 두 가지 차이점이 있습니다.

  • 임의 정밀도, BigInteger와 유사하게 임의의 정밀도 및 크기를 포함 할 수 있음
  • Base 2 대신 Base 10에서 BigDecimal은 n * 10 ^ scale입니다. 여기서 n은 임의의 큰 부호있는 정수이며 scale은 소수점을 왼쪽 또는 오른쪽으로 이동하는 자릿수로 생각할 수 있습니다.

화폐 계산에 BigDecimal을 사용해야하는 이유는 숫자를 나타낼 수있는 것이 아니라 십진수 개념으로 표현 될 수 있고 화폐 세계의 거의 모든 숫자를 포함 할 수있는 모든 숫자를 나타낼 수 있기 때문입니다. 누군가에게).


2
이 답변은 BigDecimal을 두 배 이상 사용하는 이유와 차이점을 설명합니다. 성능 문제는 부차적입니다.
Vortex

이것은 100 % 사실이 아닙니다. BigDecimal은 "n * 10 ^ scale"이라고 썼습니다. Java는 음수에 대해서만 해당합니다. 올바른 것은 "unscaledValue × 10 ^ -scale"입니다. 양수의 경우 BigDecimal은 "임의 정밀도 정수 비 스케일 값과 32 비트 정수 스케일"로 구성되며 스케일은 소수점 오른쪽의 자릿수입니다.
에 NOD

25

1 / 7십진수 와 같은 분수 값을 쓰면 얻을 수 있습니다.

1/7 = 0.142857142857142857142857142857142857142857...

무한 시퀀스의 142857. 유한 자릿수 만 쓸 수 있으므로 반올림 (또는 잘림) 오류가 발생합니다.

같은 숫자 1/10또는 1/100소수부와 이진수로 표현은 소수점 자리수 후 무한 수있다 :

1/10 = binary 0.0001100110011001100110011001100110...

Doubles 값을 이진수로 저장하므로 산술을 수행하지 않고도 십진수를 이진수로 변환하는 것만으로 오류가 발생할 수 있습니다.

BigDecimal반면에 10 진수 (예 :) 는 각 10 진수를 그대로 저장합니다. 즉, 10 진수 유형은 일반적인 의미에서 2 진 부동 소수점 또는 고정 소수점 유형보다 정확하지 않지만 (즉 1/7, 정밀도 손실없이 저장할 수 없음 ) 소수 자릿수가 유한 인 숫자의 경우 더 정확합니다. 종종 돈 계산의 경우입니다.

Java BigDecimal는 소수점 양쪽에 임의의 (그러나 유한 한) 자릿수를 가질 수 있으며 사용 가능한 메모리에 의해서만 제한 될 수 있다는 추가 이점이 있습니다.


7

BigDecimal은 Oracle의 임의 정밀도 숫자 라이브러리입니다. BigDecimal은 Java 언어의 일부이며 재무에서 과학에 이르기까지 다양한 애플리케이션에 유용합니다.

특정 계산에 복식을 사용하는 데 아무런 문제가 없습니다. 그러나 Math.Pi * Math.Pi / 6, 즉 2의 실제 인수 (현재 작업중인 프로젝트)에 대한 Riemann Zeta 함수의 값을 계산하려고한다고 가정합니다. 부동 소수점 나누기는 반올림 오류의 고통스러운 문제를 나타냅니다.

반면 BigDecimal에는 식을 임의의 정밀도로 계산하기위한 많은 옵션이 포함되어 있습니다. BigDecimal Java World에서 아래 +, * 및 /의 Oracle 위치에 설명 된대로 더하기, 곱하기 및 나누기 메소드는 다음과 같습니다.

http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

compareTo 메소드는 while 및 for 루프에서 특히 유용합니다.

그러나 BigDecimal에 대한 생성자를 사용할 때는주의하십시오. 문자열 생성자는 많은 경우에 매우 유용합니다. 예를 들어 코드

BigDecimal 1/3 = 새로운 BigDecimal ( "0.33333333333");

무한 반복 숫자를 지정된 정확도로 나타 내기 위해 1/3의 문자열 표현을 사용합니다. 반올림 오류는 대부분 JVM 내부의 어딘가에있을 가능성이 높으므로 반올림 오류는 실제 계산의 대부분을 방해하지 않습니다. 그러나 나는 개인적인 경험을 통해 반올림을 보았습니다. setScale 메소드는 Oracle 문서에서 볼 수 있듯이 이와 관련하여 중요합니다.


BigDecimal은 Java의 임의 정밀도 숫자 라이브러리 일부 입니다 . '사내'는이 맥락에서 특히 의미가 없습니다. 특히 IBM이 작성했습니다.
Lorne의 후작

@ EJP : BigDecimal 클래스를 살펴보고 그 중 일부만 IBM이 작성한다는 것을 알았습니다. 아래 저작권 의견 : /* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
realPK

7

계산을 다루는 경우 계산 방법과 사용해야 할 정밀도에 대한 법률이 있습니다. 당신이 실패하면 당신은 불법적 인 일을 할 것입니다. 유일한 실제 이유는 소수의 비트 표현이 정확하지 않기 때문입니다. 바질은 간단히 말해서 가장 좋은 설명이 그 예입니다. 그의 예를 보완하기 위해 다음과 같은 일이 발생합니다.

static void theDoubleProblem1() {
    double d1 = 0.3;
    double d2 = 0.2;
    System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));

    float f1 = 0.3f;
    float f2 = 0.2f;
    System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));

    BigDecimal bd1 = new BigDecimal("0.3");
    BigDecimal bd2 = new BigDecimal("0.2");
    System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}

산출:

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1

또한 우리는 그것을 가지고 있습니다 :

static void theDoubleProblem2() {
    double d1 = 10;
    double d2 = 3;
    System.out.println("Double:\t 10 / 3 = " + (d1 / d2));

    float f1 = 10f;
    float f2 = 3f;
    System.out.println("Float:\t 10 / 3 = " + (f1 / f2));

    // Exception! 
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}

우리에게 출력을 제공합니다 :

Double:  10 / 3 = 3.3333333333333335
Float:   10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion

그러나:

static void theDoubleProblem2() {
    BigDecimal bd3 = new BigDecimal("10");
    BigDecimal bd4 = new BigDecimal("3");
    System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}

출력이 있습니다 :

BigDec:  10 / 3 = 3.3333 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.