[JAVA]정확한 계산 BigDecimal

무지성개발자·2023년 7월 28일

double, float을 사용할 수 없는 이유

정확한 값의 계산이 필요한곳(ex.돈, 과학, 공학 등)에는 double, float은 절대 사용하면 안되고 BigDecimal이 필수라고 한다.
왜일까?

doublefloat은 실수를 표현하기 위해 자바에서 제공한다.
이 둘이 실수를 표현하는 방식은 부동소수점이란 걸 활용하는데 문제는 정확한 값이 아니라 근사치를 표현한다는 것이다.
때문에 정확한 계산, 큰 수의 계산에는 BigDecimal은 선택이 아닌 필수다.


BigDecimal

BigDecimal은 불변하며 임의의 정밀도를 가진 부호 있는 십진수 숫자를 표현하는 클래스다.

임의의 정밀도란
정수를 숫자의 배열로 간주하고 자릿수 단위로 쪼개어 배열 형태로 표현함으로써 무제한 자릿수의 정수를 담을 수 있는 방식을 말한다.

BigDecimal은 두 부분으로 나누어 숫자를 표현한다.

  • 정수 부분(Unscaled value) : 임의의 정밀도를 가진 정수를 표현한다. 즉, 십진수의 소수점을 없앤 상태의 정수를 의미.

  • 소수점 자릿수(Scale) - 십진수의 소수점 이하 자릿수를 나타낸다. Scale은 32비트 정수로 표현되며, 소수점 이하에 있는 숫자의 개수를 뜻함.

12.123으로 예를 들어보자
정수부분은 12123, 소수점 자리수는 3을 조합하여 12.123을 표현한다.


BigDecimal 생성방식

BigDecimal은 String, double, float, int, long, char[], BigInteger로 생성가능하다.
하지만 double, float으로 생성하는건 비추천한다.
코드로 봐보자.

public class NumTest {
    public static void main(String[] args) {
        BigDecimal stringBigDecimal = new BigDecimal("0.1");
        BigDecimal charBigDecimal = new BigDecimal(new char[] {'0','.','2'});
        BigDecimal intBigDecimal = new BigDecimal(1);
        BigDecimal longBigDecimal = new BigDecimal(2L);
        BigDecimal doubleBigDecimal = new BigDecimal(new Double(0.3));
        BigDecimal floatBigDecimal = new BigDecimal(new Float(0.4));

        System.out.println(stringBigDecimal);   //0.1
        System.out.println(charBigDecimal);     //0.2
        System.out.println(intBigDecimal);      //1
        System.out.println(longBigDecimal);     //2
        System.out.println(doubleBigDecimal);   //0.299999999999999988897769753748434595763683319091796875
        System.out.println(floatBigDecimal);    //0.4000000059604644775390625
    }
}

double, float으로 BigDecimal 생성을 추천하지 않는지 딱 알 수 있다.
BigDecimal을 실수로 초기화 하고 싶을 땐 BigDecimal.valueOf() 또는 그냥 String으로 표현하면 된다.


BigDecimal이 제공하는 기능

BigDecimal산술연산자(+,-,*,/)와 비교 연산자(<,>)를 사용하지 않는 대신 전용 메서드를 제공한다.

public class NumTest {
    public static void main(String[] args) {
        BigDecimal bd1 = new BigDecimal("1");
        BigDecimal bd2 = new BigDecimal("3");
        
        BigDecimal add = bd1.add(bd2);
        BigDecimal subtract = bd1.subtract(bd2);
        BigDecimal divide = bd1.divide(bd2, 2, RoundingMode.HALF_EVEN);
        BigDecimal multiply = bd1.multiply(bd2); 

        System.out.println(add);        //4
        System.out.println(subtract);   //-2
        System.out.println(divide);     //0.33
        System.out.println(multiply);   //3
        System.out.println(bd1.compareTo(bd2)); //-1
        System.out.println(bd2.compareTo(bd1)); //1
    }
}

BigDecimal은 immutable(불변성)의 특성을 가지고 있어 연산 후 BigDecimal로 return한다.


BigDecimal의 Rounding(반올림)

  • RoundingMode.CEILING : 항상 양의 방향으로 올림.

  • RoundingMode.FLOOR : 항상 음의 방향으로 내림.

  • RoundingMode.UP : 0을 기준으로 올림.(숫자가 양수이면 올림을 하고, 음수이면 내림)

  • RoundingMode.DOWN : 0을 기준으로 내림.(숫자가 양수이면 내림을 하고, 음수이면 올림)

  • RoundingMode.HALF_UP : 반올림 자리 수가 5를 기준으로 반올림

  • RoundingMode.HALF_DOWN : 반올림 자리 수가 5까지 내림

  • RoundingMode.HALF_EVEN : 반올림 자리 수가 5를 기준으로 바로 왼쪽 수가 짝수면 내림, 홀수면 올림, 반올림 자리수가 5가 아니면 일반 반올림.

  • RoundingMode.UNNECESSARY : 반올림을 하지 않으며, 스케일 자리수가 다르면 ArithmeticException 발생.


한 줄평 : 자바에서 실수는 double을 당연하게 생각했다. 만약 BigDecimal을 모른채로 돈과 관련된 실서비스를 운영했다면..

참고-
https://www.baeldung.com/java-bigdecimal-biginteger

profile
no-intelli 개발자 입니다. 그래도 intellij는 씁니다.

0개의 댓글