[Trouble Shooting] 부동소수점 오차 / float, double (실수 자료형) 정확도, BigDecimal

2019. 10. 2. 17:47개발/기타

최근 소수점을 중요하게 다뤄야하는 작업을 하고 있던 중 실수 계산 값이 미묘하게 다른 것을 알았고 오류를 찾기 위해 소스 코드를 다시 보았습니다. 디버깅을 하던 중, 언젠가  실수 계산의 정확도  에 대해 들었던 것이 생각나 구글링을 해보니 많은 분들이 float, double의 정확도에 대해서 쓴 글을 찾을 수 있었습니다. 이번 기회에 제대로 알고 넘어가고자 정리할 겸 포스팅합니다.

 

실수의 표현 방식

컴퓨터에서 실수를 표현하는 방법은 정수에 비해 훨씬 복잡합니다. 실수를 정수와 마찬가지로 2진수로만 표현해야하기 때문입니다. 따라서 실수를 표현하기 위해 현재에는 다음과 같은 방식이 사용되고 있습니다.

 

1. 고정 소수점 방식 : Fixed Point

실수는 보통 정수부와 소수부로 나눌 수 있습니다. 따라서 실수를 표현하는 가장 간단한 방식은 소수부의 자릿수를 미리 정하여, 고정된 자릿수의 소수를 표현하는 것입니다. 하지만 이 방식은 정수부와 소수부의 자릿수가 크지 않으므로 표현할 수 있는 범위가 매우 적다는 단점이 있습니다.

 

예를 들어, -3.14라는 소수를 고정 소수점 방식으로 표현하기 위해  부호,  정수부,  0.14  소수부 와 같이 3가지 요소로 나눌 수 있습니다. 

 

2. 부동 소수점 방식 : Floating Point 

실수를 가수부와 지수부로 나누어 표현하는 방식입니다. 부동 소수점 방식은 다음 수식을 이용하여 매우 큰 실수까지도 표현할 수 있습니다.

±(1.가수부)×2지수부-127

가수: 실수의 실제값을 표현

지수: 크기를 표현, 가수의 어디쯤에 소수점이 있는지 나타냄

즉, 지수의 값에 따라 소수점이 움직이기 때문에 부동소수점이라고 합니다. 

 

부정확성의 원인

IEEE-754 부동소수점 표준에 의해 정수, 소수 등과 같은 숫자가 2진으로 저장되기 때문에 나타납니다. 예를 들어 1,2,3과 같은 정수는 2진수로 표현이 가능합니다. 하지만 1/7(0.142857142857... ), 0.1과 같은 소수(무한, 순환, 유한)들은 2진수로 표현할 방법이 없기 때문입니다. 0.142857...을 소수점 몇 번째 자리 아래에서 반올림을 하여도 그것은 반올림을 한 값이지 0.142857... 과 같은 값은 아닙니다. 컴퓨터는 그 값과 가장 근사한 값을 반환하는 것이고, 이때 부동소수점의 부정확성이 나타납니다. 

 

float과 double은 이진 부동 소수점 연산을 수행합니다. 이는 넓은 범위의 값에 대해 정확도가 높은 근사치를 제공할 수 있도록 세심하게 설계된 연산이지만 정확한 결과를 제공하지는 않기 때문에 정확한 결과가 필요한 곳에는 사용하면 안됩니다. 

부정확성의 해결 방법

BigDecimal 자료형을 사용하면 정확한 값을 계산할 수 있습니다. 하지만 덧셈과 같은 기본적인 연산을 할 때에도 최소 2개의 BigDecimal 객체를 선언해야하기 때문에 리소스를 많이 잡아먹을 수 있습니다.

 

BigDecimal에 대한 설명과 멤버 함수는 공식문서를 통해 확인할 수 있습니다. 


 References 

http://tcpschool.com/cpp/cpp_datatype_floatingPointNumber

https://do-rang.tistory.com/73