Java

부동소수점에서 엡실론

오개발 2024. 10. 31. 14:12

프로그래밍상으로 소수점을 계산 할 일이 많습니다 특히 돈 관련 계산을 할때, 

 

이번에도 결론적으로 말하자면, 실수를 다룰 때는 BigDecimal을 사용하면 됩니다.

자바에서 다음과 같이 계산을 하였습니다

 

        double a = 0.1;
        double b = 0.2;

        System.out.println(a+b);
        System.out.println(a+b == 0.3);

 

결과는

0.30000000000000004

 

이유는 2진법으로 근사값을 저장하게 되며, 두 수를 더할 때 이 근사값으로 인해 0.3 대신 0.30000000000000004라는 오차가 나타납니다

 

그래서 개발자가 지정한 부동소수점에서 엡실론을 정하여 오차를 대략적으로 짐작하여 뺴주는 방식이다. 하지만 이러한 방식은 추천 하지 않는다.

 

그래서 가장 좋은 방법으론 BigDecimal을 사용 하면 됩니다.

 

BigDecimal a = new BigDecimal("0.1").add(newBigDecimal("0.2"));
BigDecimal b = new BigDecimal("0.3");

System.out.println(a.equals(b));  // true
System.out.println(a);            // 0.3

이제야 0.1 + 0.2가 0.3으로 계산되었습니다.
new 연산자를 사용한 방법 외에 BigDecimal.valueOf를 통해 초기화 하는 방법도 존재합니다.

BigDecimal a = BigDecimal.valueOf(0.1).add(BigDecimal.valueOf(0.2));
BigDecimal b = BigDecimal.valueOf(0.3);
System.out.println(a.equals(b));  // true

이번에도 0.1 + 0.2가 정상적으로 계산되는 것을 확인할 수 있습니다.

둘의 동작은 비슷한듯 다르기 때문에 주의해야 합니다.
성능까지 자세히 들어가면 캐싱 등 고려할 점이 존재하지만 이 글에서는 초기화 관점에서만 다루겠습니다.

new 연산자

new 연산자를 사용할 때는 가능한 문자열의 형태로 생성자에 인자를 전달해야 합니다.
new 연산자는 주어진 값을 최대한 정확하게 나타내려고 하기때문에, 그 값이 바뀔 수 있습니다. 다음 코드가 그 예시입니다.

System.out.println(new BigDecimal(0.01));   // 0.01000000000000000020816681711721685132943093776702880859375
System.out.println(new BigDecimal("0.01"));   // 0.01

Double 값을 그대로 전달하면 0.01이 아니라 0.010000... 으로 초기화 된 것을 확인할 수 있습니다.
반면 문자열의 형태로 초기화한 경우에는 문제없이 초기화 됩니다.

 

BigDecimal.valueOf
BigDecimal.valueOf의 동작이 조금 더 직관적입니다. 간단하게 생각해서, System.out.println() 으로 출력할 때 나타나는 값 그대로 초기화된다고 생각하면 됩니다.
주의할 점은 이번엔 문자열이 아니라 double 값을 그대로 전달해야 합니다.

System.out.println(BigDecimal.valueOf(0.01));   // 0.01
//System.out.println(BigDecimal.valueOf("0.01")); // 문자열을 전달하면 컴파일 에러

상황에 따라 필요한 방법을 쓰면 되겠지만 다음과 같은 코드는 BigDecimal을 잘못사용한 경우라는 것만 알아두면 될 것 같습니다.

ex)

BigDecimal a = BigDecimal.valueOf(0.1+0.2); // (X)

a에 0.3이 저장될 것이라고 기대하면 잘못이해한 것입니다.
초기화는 valueOf나 new 연산자를 사용하고, 연산은 eqauls나 add와 같은 메서드를 이용해야 합니다.

 

 

 

 

 

 

'Java' 카테고리의 다른 글

JavaBean과 POJO  (0) 2024.11.04
ArrayList의 자동 크기 증가  (0) 2024.10.31
오토 박싱 & 오토 언박싱  (1) 2024.10.30
자바_instanceof (객체타입 확인)  (1) 2024.10.30
초기화 블록  (0) 2024.10.29