본문 바로가기
BackEnd/JAVA

실수형 표현이 정확하지 않은 이유?

by sorryisme 2024. 10. 5.

자바 뿐만 아니라 모든 컴퓨터 언어에서 소수 계산을 하다보면 문제가 발생하는데 사실 문제 해결하는데 급급했으나

대략적으로만 알고 있지 정확히 설명할 수 없었다. 

 

그래서 이번 기회를 통해 한 번 정리하고자 한다

 

 

컴퓨터에서 실수형 표현

  • 컴퓨터 데이터는 전기신호에 기반한 비트(2진수)
  • 하지만 정수와 다르게 실수형에서 소수의 표현은 이진수로 표현의 한계가 발생한다
  • 예를들어 3.141592… 같은 숫자의 표현은 무한소수
  • 0.1을 이진수로 표현해도 무한소수이다
  • 근데 근사값으로도 충분하다고 판단하여 고정 소수점과 부동 소수점을 사용한다

 

고정 소수점

  • 1비트의 부호 비트와 15비트의 정수부, 16비트의 소수부로 구성되어 있다
  • 하지만 표현할 수 있는 수의 한계가 명확하다

부동 소수점

  • 표현할 수 있는 값을 최대한 넓혀 오차를 줄이기 위함
  • 메모리에 지수부(8bit), 가수부(23bit)로 나눈다
  • 가수부에는 실제 데이터 비트가 들어가고, 지수부에 소수점 위치를 가리키는 제곱승이 들어간다

부동 소수점 표현방식

자바에서 소수의 표현

  • IEEE754에 따른다
    • float, 단일 정확도(32bit): (부호부 1 비트, 지수부 8 비트, 가수부 23비트)
    • double, 이중 정확도(64bit) : (부호부 1 비트, 지수부 11 비트, 가수부 52 비트)

 

프로그래밍 소수의 계산

  • 아무리 부동 소수점이라도 무한 소수의 표현의 한계가 발생한다
  • 어느정도 끊어서 반올림 해줘야하는데 계산 할 때 부정확한 결과를 가져온다
public class Main {
    public static void main(String[] args) {
        double val1 = 12.23;
        double val2 = 34.45;
        System.out.println(val1 + val2);
    }
}

// 46.68000000000001


자바만 그럴까?

자바스크립트 언어

  • 자바스크립트로 동일하게 테스트했을 때 같은 결과가 나온다
  • 어떤 언어에서든지 동일한 문제가 발생한다

 

 

 

해결방법1. 정수로 치환

public class Main {
    public static void main(String[] args) {

        double val1 = 12.23 ;
        double val2 = 34.45;

        int value = (int)((val1 * 100)  + (val2 * 100));

        System.out.println((double)value / 100) ;

    }
}
  • 소수 자리수가 적거나 고정되어있을 경우는 가능할 것으로 판단된다
  • 허나 코드 가독성 부분에서 불편하다

해결방법2. BigDecimal 클래스

public class Main {
    public static void main(String[] args) {

        BigDecimal val1 = new BigDecimal("12.23");
        BigDecimal val2 = new BigDecimal("34.45");
        System.out.println(val1.add(val2));

    }
}

 

 

참고 사이트

https://inpa.tistory.com/entry/JAVA-☕-실수-표현부동-소수점-원리-한눈에-이해하기

https://www.tcpschool.com/c/c_refer_floatingPointNumber