개발

컴퓨터가 소숫점 계산을 잘 하지 못하는 이유

Debin 2024. 6. 13.
반응형

 

이상한 소숫점 계산

 

컴퓨터는 소숫점 계산을 잘 하지 못한다고 한다.

자바를 사용해 아래와 같은 테스트 코드를 만들어 보았다.

 

public class CalculateExampleTest {

    @Test
    void test() {
        System.out.println(1.1 + 0.1 == 1.2);
    }
}

 

어떤 값이 출력될까? 편하게 생각하면 '당연히 1.1 + 0.1은 1.2니까 true가 출력되겠네!' 라고 생각할 수 있다.

눈으로 확인한 출력 값은 우리의 예상과 다르다.

 

 

false가 출력됐다. 어떻게 false가 출력된 것일까..??

이제 왜 이런일이 일어나는지 알아보자.

 

컴퓨터는 기본적으로 이진법을 사용한다. 0과 1만 사용하는 것이다.

컴퓨터가 이진법을 사용하는 이유는 효율성은 물론이고 하드웨어적인 측면을 살펴보면 CPU, RAM 모두 트랜지스터로 구성되기 때문이다.

좀 더 깊은 내용을 원하면 아래 영상을 강력 추천한다..!

 

https://www.youtube.com/watch?v=Fg00LN30Ezg&t=436s

 

 

이어서 간단히 이진법의 예시를 살펴보겠다.

1 byte가 8bit이니 8bit 기준으로 예시를 작성했다.

  • 5 == 00000101
  • 16 == 00010000
  • 19 == 00010011
  • 128 == 10000000

지금까지 살펴본 방식으로는 소수를 저장할 수 없다.

어떻게 해야지 소수를 저장할 수 있을까??

 

소수를 이진법으로 변환

 

소수 부분을 이진법으로 변환하는 과정은 다음과 같다.

  1. 소수를 2로 곱한다.
  2. 정수 부분을 기록한다.
  3. 소수 부분만 남긴다.
  4. 이 과정을 소수 부분이 0이 되거나 원하는 정확도에 도달할 때까지 반복한다.

 

17.625를 예시로 살펴보자.

 

정수 부분은 10001이다.

 

.625를 계산해보면

0.625 * 2 = 1.25

0.25 * 2 = 0.5

0.5 * 2 = 1.0

 

결론은 .101이 된다. 따라서 결론은 10001.101 이다.

 

101을 검산하려면 다음과 같이 진행하면된다.

첫 번째 자리는 1 & 2^-1 이다. 따라서

1 * 0.5 = 0.5

두 번째 자리는  0 * 2^-2이다. 따라서

0

세 번째 자리는 1 * 2^-3이다. 따라서

1 * 0.125 = 0.125 

 

0.625 검산 성공!!

 

십진법의 소숫점을 이진수로 바꾸는 방식을 확인했다. 그럼 이제 컴퓨터가 소숫점을 저장하는 방법을 더 자세히 알아보자.

 

컴퓨터의 소수 저장

자바에서 Int와  Float는 4 byte다. 총 32비트로 총 32개의 1과 0이 값으로 들어간다.

Float은IEEE 754 표준에 따라 부호 비트 1비트, 지수 8비트, 가수 23비트로 구성된다.

 

 

5.125는 이진법으로 표현하면 101.001 이다.

좌측으로 땡기면 1.01001*2^2라고 표현할 수 있다.

그럼 이제 01001이 가수 부분 23 비트 앞자리부터 채워진다.

 

빨간색 비트는 부호 비트다.

파란색 비트는 지수 비트다.

초록색 비트는 가수 비트다. 5.125는 아래와 같이 표현이 가능하다.

0 00000001 01001000000000000000000

 

여기에 127을 더하면 아래와 같이 이진법 표현이 변한다.

0 10000001 01001000000000000000000

 

이 방식에서 문제가 생긴다. 0.125는 이진법으로 깔끔하게 떨어지는데 0.1 같은 경우는 순환 소수가 발생한다.

이진법으로 변환한 결과가 0.00011001100110011...(반복) 다음과 같다.

그럼 변수의 메모리 크기가 32bit이므로 범위를 벗어나는 숫자들은 짤리게 된다.

여기서 중요한 부분이 그렇다면 짤린 숫자만큼 오차가 발생하는 것이다...!!!

 

이런 오차가 발생하기 때문에 우리의 위 코드 결과 (1.2 + 0.1 == 1.3)가 false로 출력되는 것이다.

 

그렇다면 어떻게 계산해야할까??

  • 정확히 계산하려면 정수로 계산하는 것이 좋은 습관이다.
  • 반올림 방식을 사용하자.
  • Float 대신 64bit인 Double 자료형을 사용하는게 낫다.
  • 자바에서 정확한 계산을 사용하려면 BigDecimal을 사용하면 된다.

 

BigDecimal 클래스 같이 정교한 계산을 지원하는 클래스나 정수로 변환하여 처리하는 방법이 제일 좋은 방법이라고 생각한다.

 

그럼 이상으로 포스팅을 마치겠습니다.

 

참고자료

https://www.youtube.com/watch?v=-GsrYvZoAdA

 

 

 

 

 

 

반응형

댓글