자바 에서 BigDecimal 정밀도 문 제 를 상세 하 게 풀다.

배경
실제 개발 에 서 는 정밀 도 를 정확하게 계산 하지 않 아 도 되 는 속성 에 대해 서 는 float 나 double 을 직접 사용 할 수 있 으 나 정확 한 계산 결과 가 필요 하 다 면 가격,품질 등 BigDecimal 을 사용 해 야 한다.
왜 이렇게 말 합 니까?주로 두 가지 가 있 습 니 다.
1.double 계산 은 정밀도 손실 문제 가 있 습 니 다.
2.나눗셈 연산 시 BigDecimal 은 풍부 한 취사선택 규칙 을 제공 합 니 다.(double 은 NumberFormat 을 통 해 반올림 할 수 있 지만 NumberFormat 은 스 레 드 가 안전 하지 않 습 니 다)
정밀도 문제 에 대하 여 우 리 는 실제 의 예 를 볼 수 있다.

public static void main(String[] args) {
    //   3.3
    System.out.println("    :"+(1.1+2.2));
    //   -7.9
    System.out.println("    :"+(2.2-10.1));
    //   2.42
    System.out.println("    :"+(1.1*2.2));
    //   0.44
    System.out.println("    :"+(4.4/10));
}
실제 콘 솔 출력

왜 이러 니
우리 컴퓨터 는 2 진법 이다.부동 소수점 은 2 진법 으로 정확하게 표시 할 방법 이 없다.우리 의 CPU 는 부동 소수점 이 두 부분 으로 구성 되 어 있다 는 것 을 나타 낸다.지수 와 끝자리,이런 표현 방법 은 일반적으로 모두
일정한 정확 도 를 잃 으 면 일부 부동 소수점 연산 도 일정한 오차 가 생 길 수 있다.예 를 들 어 2.4 의 이 진 은 정확 한 2.4 가 아니 라 는 것 을 나타 낸다.오히려 가장 가 까 운 이 진 은 2.399999999999999999999 라 고 밝 혔 다.
부동 소수점 의 수 치 는 실제로 특정한 수학 공식 에 의 해 계산 된다.
2.BigDecimal 구조 함수
1.네 가지 구조 함수

BigDecimal(int)     //                 。
BigDecimal(double)  //                  。
BigDecimal(long)    //                  。
BigDecimal(String)  //                       。
이 몇 개 는 모두 자주 사용 하 는 구조 기 이 고 그들 이 돌아 오 는 대상 은 모두 BigDecimal 대상 이다.다시 말 해 빅 디 밀 대상 을 다른 유형의 대상 으로 바 꾸 는 것 은 다음 과 같은 몇 가 지 를 통 해 알 수 있다.

toString()          // BigDecimal           。
doubleValue()       // BigDecimal            。
floatValue()        // BigDecimal            。
longValue()         // BigDecimal           。
intValue()          // BigDecimal          。
여 기 는 BigDecimal(double)의 구조 함수 에 매우 주의해 야 합 니 다.또한 정밀도 가 잃 어 버 리 는 문제 가 존재 할 수 있 습 니 다.다른 것 은 아 닙 니 다.여기 서도 예 를 들 어 설명 할 수 있 습 니 다.

public static void main(String[] args) {
    BigDecimal intDecimal = new BigDecimal(10);
    BigDecimal doubleDecimal = new BigDecimal(4.3);
    BigDecimal longDecimal = new BigDecimal(10L);
    BigDecimal stringDecimal = new BigDecimal("4.3");
    System.out.println("intDecimal=" + intDecimal);
    System.out.println("doubleDecimal=" + doubleDecimal);
    System.out.println("longDecimal=" + longDecimal);
    System.out.println("stringDecimal=" + stringDecimal);
}
콘 솔 실제 출력

그림 에서 볼 수 있 듯 이 double 의 구조 함수 에 대해 정밀도 가 잃 어 버 릴 가능성 이 있다.
2,왜 이런 상황
이것 은 new BigDecimal(double)유형의 구조 함수 에 대한 설명 이 있 습 니 다.
이 구조 함수 의 결 과 는 예측 할 수 없 을 것 이다.자바 에 new BigDecimal(0.1)을 기록 하여 BigDecimal 을 만 들 었 다 고 가정 할 수 있 습 니 다.이것 은 완전히 0.1(비 눈금 값 은 1,비례 는 1)과 같 지만 사실은 같 습 니 다.
0.1000000000000000055511151231257827021181583404541015625。 0.1 이 더 블(또는 그 어떠한 제 한 된 길이 의 이 진 점수)처럼 정확하게 표시 할 수 없 기 때문이다.
따라서 구조 에 전달 되 고 있 는 값 은 0.1 이 아니다.
3.어떻게 해결 할 것 인가
두 가지 상용 해결 방법 이 있다.
1.double.toString(double)을 통 해 먼저 String 으로 전환 한 다음 에 BigDecimal 의 String 구조 함수 에 넣 습 니 다.
2.BigDecimal 의 구조 함 수 를 통과 하지 않 고 정적 인 방법 인 BigDecimal.valueOf(double)를 통 해서 도 정밀 도 를 잃 지 않 습 니 다.
예시

 public static void main(String[] args) {
    String string = Double.toString(4.3);
    BigDecimal stringBigDecimal = new BigDecimal(string);
    BigDecimal bigDecimal = BigDecimal.valueOf(4.3);
    System.out.println("stringBigDecimal = " + stringBigDecimal);
    System.out.println("bigDecimal = " + bigDecimal);
}
실행 결과

이렇게 하면 더 블 에 게 빅 데 시 멜 을 돌리 면 정밀도 가 떨 어 지지 않 는 다 는 것 을 보증 할 수 있다.
3.상용 방법
1.상용 방법
예시

public static void main(String[] args) {
    BigDecimal a = new BigDecimal("4.5");
    BigDecimal b = new BigDecimal("1.5");
    BigDecimal c = new BigDecimal("-10.5");

    BigDecimal add_result = a.add(b);
    BigDecimal subtract_result = a.subtract(b);
    BigDecimal multiply_result = a.multiply(b);
    BigDecimal divide_result = a.divide(b);
    BigDecimal remainder_result = a.remainder(b);
    BigDecimal max_result = a.max(b);
    BigDecimal min_result = a.min(b);
    BigDecimal abs_result = c.abs();
    BigDecimal negate_result = a.negate();

    System.out.println("4.5+1.5=" + add_result);
    System.out.println("4.5-1.5=" + subtract_result);
    System.out.println("4.5*1.5=" + multiply_result);
    System.out.println("4.5/1.5=" + divide_result);
    System.out.println("4.5/1.5  =" + remainder_result);
    System.out.println("4.5 1.5   =" + max_result);
    System.out.println("4.5 1.5   =" + min_result);
    System.out.println("-10.5    =" + abs_result);
    System.out.println("4.5    =" + negate_result);
}
4.5+1.5=6.0
4.5-1.5=3.0
4.5*1.5=6.75
4.5/1.5=3
4.5/1.5 여수=0.0
4.5 와 1.5 최대 수=4.5
4.5 와 1.5 최 소수=1.5
-10.5 의 절대 치=10.5
4.5 의 반대 수=-4.5
여기 서 나눗셈 을 따로 다시 한 번 말씀 드 리 겠 습 니 다.나눗셈 을 조작 할 때 다 제거 할 수 없 는 상황 이 있 기 때 문 입 니 다.예 를 들 어 3,5/3,이때 자바.lang.Arithmetic Exception:Non-terminating decimal expansion 을 잘못 보고 할 수 있 습 니 다.
no exact representable decimal result。그래서 여 기 는 끝 이 없 는 상황 에서 몇 개의 소수,취사 규칙 을 보류 하 는 것 을 고려 해 야 한다.나눗셈 이 들 어가 지 않 을 수 있다 면 다음 방법 을 사용 하 세 요)

BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)         ,               ,           。
2.취사선택 규칙

ROUND_UP          //            (0  )   1
ROUND_DOWN        //      ,        
ROUND_HALF_UP     //        
ROUND_HALF_DOWN   //    
ROUND_CEILING     //        
ROUND_FLOOR       //        
ROUND_HALF_EVEN   // (  )       ,    (   )   ,     ,         ,  ROUND_HALF_UP,     ,  ROUND_HALF_DOWN
ROUND_UNNECESSARY //        ,        
주의 하 세 요.저희 가 제일 많이 쓰 는 건 ROUND 인 것 같 아 요.HALF_UP(반올림)
여기 서 자주 사용 하 는 취사선택 규칙 을 몇 개 들 었 다.

 public static void main(String[] args) {

    BigDecimal a = new BigDecimal("1.15");
    BigDecimal b = new BigDecimal("1");

    //            (0  )   1        1.2
    BigDecimal divide_1 = a.divide(b,1,BigDecimal.ROUND_UP);
    //      ,                        1.1
    BigDecimal divide_2 = a.divide(b,1,BigDecimal.ROUND_DOWN);
    //                      1.2
    BigDecimal divide_3 = a.divide(b,1,BigDecimal.ROUND_HALF_UP);
    //                    1.1
    BigDecimal divide_4 = a.divide(b,1,BigDecimal.ROUND_HALF_DOWN);
    //   1.15  1.16
    BigDecimal c = new BigDecimal("1.16");
    //                 1.2
    BigDecimal divide_5 = c.divide(b,1,BigDecimal.ROUND_HALF_DOWN);
    System.out.println("divide_1 = " + divide_1);
    System.out.println("divide_2 = " + divide_2);
    System.out.println("divide_3 = " + divide_3);
    System.out.println("divide_4 = " + divide_4);
    System.out.println("divide_5 = " + divide_5);

}
실행 결과
divide_1 = 1.2
divide_2 = 1.1
divide_3 = 1.2
divide_4 = 1.1
divide_5 = 1.2
포맷
NumberFormat 류 의 format()방법 은 BigDecimal 대상 을 매개 변수 로 사용 할 수 있 기 때문에 BigDecimal 을 이용 하여 16 비트 이상 의 유효 숫자 를 가 진 화폐 값,백분 치,일반 수 치 를 포맷 제어 할 수 있 습 니 다.
BigDecimal 을 이용 하여 화폐 와 백분율 을 포맷 하 는 것 을 예 로 들 면.우선,BigDecimal 대상 을 만 들 고 BigDecimal 의 산술 연산 을 한 후 각각 화폐 와 백분율 포맷 에 대한 인용 을 만 들 고 마지막 으로 이용 합 니 다.
BigDecimal 대상 은 format()방법의 매개 변수 로 포맷 된 화폐 값 과 백분율 을 출력 합 니 다.
예시

public static void main(String[] args) {
    //         
    NumberFormat currency = NumberFormat.getCurrencyInstance();
    //          
    NumberFormat percent = NumberFormat.getPercentInstance();
    //        3 
    percent.setMaximumFractionDigits(3);
    //  
    NumberFormat integerInstance = NumberFormat.getIntegerInstance();
    ////  
    BigDecimal loanAmount = new BigDecimal("188.555");
    ////  
    BigDecimal interestRate = new BigDecimal("0.018555555");
    //                 2 
    System.out.println("  : " + currency.format(loanAmount));
    //  (   )                    
    System.out.println("  : " + percent.format(interestRate));
    //         188.555   189, 188.51  189   189.5  188,               
    System.out.println("  : " + integerInstance.format(loanAmount));
}
실행 결과
금액:¥188.56 금 리:1.856%취 정:189
여기 몇 가지 설명 이 있어 요.
1.포맷 할 때 보존 자릿수 가 지정 되 지 않 은 상태 에서 기본적으로 2 자 리 를 유지 합 니 다.
2.화폐(백분율)포맷 지정 기본 취사 규칙 은 반올림 입 니 다.
3.취 정 은 조금 다 릅 니 다.188.555 취 정 은 189 이 고 188.51 도 189 입 니 다.그러나 189.5 는 확실히 188 이기 때문에 진정한 의미 의 반올림 이 아 닙 니 다.
이상 은 자바 에서 BigDecimal 정밀도 문 제 를 상세 하 게 설명 하 는 상세 한 내용 입 니 다.자바 에 관 한 자 료 는 다른 관련 글 에 주목 하 세 요!

좋은 웹페이지 즐겨찾기