본문 바로가기

공부/CS 기초이론

소수점 표현시 주의점과 Java의 BigDecimal 사용

컴퓨터는 2진수를 사용하기때문에 소수점을 표현하는데는 한계가 있고 오차가 발생한다.

 

돈과 관련된 도메인에서는 이는 매우 치명적이며 주의해야한다.

 

public class Test {
   public static void main(String[] args) {
      double a = 0.1;
      double b = 0.2;
      double c = 0.3;

      System.out.println(a + b);
      System.out.println(a + b == c);
   }
}

 

0.30000000000000004
false

 

정확한 소수점 계산들을 사용하기 위해서는 각 언어에서는 이를 지원해주는 라이브러리들이 있다.

- Java : math.BigDecimal

- JS : Big.js 라이브러리

- python : import decimal 등

 

이들 중 java의 BigDecimal 을 다룬다.

 


 

BigDecimal은 불변의 성질을 가지며, 임의 정밀도와 부호를 가지는 10진수이다.

그리고, 원시타입이 아니므로 연산자를 사용할 수 없고, 메서드를 통하여 계산해야한다. 또한, 불변성을 가지므로 메서드를 통해 연산할 때 마다 새로운 객체를 생성한다.

 

Java의 BigDecimal 구성

package java.math;

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    
    private final BigInteger intVal; // = unscaled value

    private final int scale;

    private transient int precision;

    private final transient long intCompact;
    
    ...

 

intVal : unscaled value를 저장한 값

intCompat : 유효숫자의 절대값이 long타입으로 표현가능하면 BigInteger대신 저장하여 메모리를 최적화한다.

scale : 소수점 오른쪽의 자릿수

precision : 총 자릿수

 

 

ex) BigDecimal.valueOf(3.14)

 


 

BigDecimal 사용시 주의점

 

- 계산 결과가 무한소수 같은 경우 ArithmeticException 예외가 발생한다.

(ex. BigDemical.valueOf(10).divide(BigDecimal.valueOf(3))

 

자리 수를 지정할 수 있다. Enum클래스의 RoundingMode참고할것

// 나누기 - 소수점 아래 첫째 자리까지 반올림
a.divide(b, 1, RoundingMode.HALF_UP);

// 나누기 - 총 자릿수를 34개로 제한하고 반올림(HALF_EVEN)
a.divide(b, MathContext.DECIMAL128);

 

 

 

- 생성시 double값을 이용하지 말것

 

근사값이 그대로 저장되어버린다.

BigDecimal bigDecimal = new BigDecimal(1.1);
System.out.println(bigDecimal);
// 1.100000000000000088817841970012523233890533447265625

 

String 생성자나 valueOf같은 정적 팩토리 메서드를 이용할 것

new BigDecimal("1.12"); // 1.12
BigDecimal.valueOf(1.12); // 1.12

 

 

- 동등성 비교시 compareTo()와 equals() 주의

 

equals()는 unscaled value과 scale을 모두 비교하는 반면, compareTo()는 unscaled value만을 비교한다. equals()는 값과 소수점 이하의 자릿수를 모두 비교하고 compareTo()는 값만 비교한다.

{
    "price": 0 
}
BigDecimal price = fetchAPI().getPrice(); // BigDecimal("0.0000")
response.price.equalsTo(BigDecimal.ZERO) // false
response.price.compareTo(BigDecimal.ZERO) == 0 // true

 

참고글:

https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html

https://hudi.blog/java-biginteger-bigdecimal/

https://dev.gmarket.com/75

'공부 > CS 기초이론' 카테고리의 다른 글

[JAVA / Spring] DI의 구현 방법  (0) 2023.04.19
[Web] REST API 규칙  (0) 2023.04.14
[Web] URI, URN, URL 구분하기  (0) 2023.04.12
[Web] REST에 대해 간단 정리  (0) 2023.04.12
[Web] HTTP Method  (0) 2023.04.11