728x90

현업에서 실수타입의 점수를 계산하는 로직을 구현하였다. 점수는 소수점 일의 자릿수에서 반올림하는 것으로 정의하였고 나는 Java단에서 계산하는 로직을 구현하였다. 그리고 운영중 데이터가 반올림하여 1.1이 되어야하는데 1.0으로 표시된 이슈를 발견하여 알아보았다. Java에서 3*0.35가 1.05가 아니었다. 왜 이런 현상이 발생하는 것일까?


우리가 십진법으로 계산할 때 3/1 = 0.3333333(무한반복)이 된다. 이와 같이 컴퓨터에서는 이진법으로 계산하기 때문에 십진법에서의 3/1과 같이 오차가 발생하는 수가 존재하는 것이다. 

 

※ 프로그래밍 언어에서 계산한 경우 예시

 ① 3 * 0.35 = 1.0499999999999998

 ② 0.1 + 0.2 = 0.30000000000000004

 

가장 많이 사용하는 예시인 ②번 예시에대해 설명해보자

소주점만 곱해야한다.

0.1을 이진수로 변환하면 '0.0001100...'로 2를 곱했을 때 1.0이 떨어져야 소수의 끝이 있지만 1.0으로 떨어지지 않기 때문에 무한소수이다.

 

무한소수는 자료형에 따라 최대 자릿수에서 반올림처리 되는데 이때 나머지 자릿수들은 버려지기 때문에 수치가 정확해지지 않는 것이다. (이해하기 쉽게 무한소수로 설명했지만 지나치게 크기가 큰 유한소수들도 마찬가지라고 이해하면 된다.)

 

이를 더 자세히 이해하기 위해서 부동소수점과 고정소수점을 검색해보기 바란다.(포스팅 예정)

 

그렇다면 실수의 오차를 줄이려면 어떻게 해야할까?

 

1) Java

 ① Java에서는 BigDecimal를 제공하고 있다. 1 나누기 3을 표현하는 경우를 실행해보자.

public static void main(String[] args) throws IOException {

    float a = 3;
    float b = 1;

    double c = 3;
    double d = 1;

    BigDecimal bigDecimal1 = new BigDecimal("3");
    BigDecimal bigDecimal2 = new BigDecimal("1");

    System.out.println(b/a);
    System.out.println(d/c);
    System.out.println(bigDecimal2.divide(bigDecimal1,25,BigDecimal.ROUND_UP));
    
}

//result
//0.33333334
//0.3333333333333333
//0.3333333333333333333333334

여기서 주의해야할 점이 있는데 bigDemciaml에서 divide함수를 사용할 때 소수점 어느 자릿수에서 반올림 할 것인지 꼭 설정해주어야 한다. 그렇지않으면 무한소수의 경우 에러가 발생하게 된다.

 

 ② 3*0.35 예시

public static void main(String[] args) throws IOException {

    double c = 3;
    double d = 0.35;

    BigDecimal bigDecimal1 = new BigDecimal("3");
    BigDecimal bigDecimal2 = new BigDecimal("0.35");

    System.out.println(d*c);
    System.out.println(bigDecimal2.multiply(bigDecimal1));

}

// result
// 1.0499999999999998
// 1.05

 

 

2) DB

① Mysql에서는 decimal Data Type을 제공해주고 있다.

② Tibero에서도 decimal Data Type을 제공하고 있는데 단 NUMBER형으로 변환되어 사용된다.

 

예를들어 Mysql에서는 0.1을 11만번을 sum을 하면 '11,000.0000000225'가 출력되는데 decimal로 생성된 컬럼이나 Tibero에서 NUMBER로 생성된 컬럼은 똑같이 sum을 해도 오차없이(소수점 자리가 나타나지 않고) '11,000' 만 출력한다.

 

Tibero 설명 가이드 문서 내용 부분참조

 

다음은 Mysql에서 Decimal 관련 궁금증을 해소하는데 도움이되었던 글이다.

 

https://codingdog.tistory.com/entry/mysql-decimal-vs-double-%EA%B3%A0%EC%A0%95-%EC%86%8C%EC%88%98%EC%A0%90%EA%B3%BC-%EB%B6%80%EB%8F%99-%EC%86%8C%EC%88%98%EC%A0%90

 

mysql decimal vs double : 고정 소수점과 부동 소수점

 레코드에 double, float과 같은 실수 타입이 있습니다. 예를 들자면, 0.1달러, 0.2달러와 같은 것들입니다. 이것을 double로 저장했을 때와 decimal로 저장했을 때 어떤 차이가 있을까요? 간단하게 알아

codingdog.tistory.com

 

728x90
TOP