본문 바로가기

IT/JAVA

부동 소수점과 JAVA 실수형(float, double)의 오차 해결에 대해서

실수를 표현하는 방식은 고정소수점 방식부동소수점 방식이 있습니다.

 

고정소수점 방식(Fixed Point)은 [부호 | 정수부 | 소수부]로 나타냅니다. 

고정소수점은 소수점이 고정되어 있고, 실수를 2진수로 변환한 값을 그대로 대입하는 방식입니다.

 

 

부동소수점 방식(Floating Point)은 [부호 | 지수부 | 가수부]로 나타냅니다.

실수를 표현할 때 소수점의 위치를 고정하지 않고 형태에 맞게 소수점을 나타내는 방식입니다. (정규화 과정)

 

 

고정 소수점 방식의 경우 구현하기 편하지만, 정수부와 소수부의 자릿수가 작아 표현할 수 있는 범위가 제한적입니다.

(만약, 10비트를 표현할 수 있던 자료형의 5자리를 소수부에 나눠준다면 수의 범위는 정수부 기준 5자리 밖에 되지 않게 됩니다.)

 

반면, 부동 소수점 방식의 경우 정규화를 통해 지수부와 가수부로 나타냄으로써

같은 비트수일 때, 고정 소수점 방식보다 더 넓은 범위를 표현할 수 있습니다.

 

이러한 이유로 고정 소수점 방식보단 부동 소수점 방식을 많이 사용하게 됩니다.

부동 소수점 방식

JAVA 실수형 관점에서 보면, 두 가지가 있습니다.

 

IEEE 부동소수점 방식

표현 과정

실수를 부동소수점 방식으로 표현하는 과정에 대해 알아봅시다.

1. 실수를 2진수로 변환합니다.
ex) 7.2(10) => 111.00110011...(2)

2. 2진수를 1.xxx...* 2^n 형식이 되도록 정규화합니다.
ex) 1.1100110011... * 2^2

3. 지수부에는 (2의 지수값 + bias 값)을 2진수로 변환한 결과를 넣어줍니다.
- 지수부에는 부호 비트가 없기 때문에 음수 지수를 처리하기 위해 bias 값을 가집니다.

* 32bit의 경우 bias값 127, 64bit의 경우 bias값 1023

따라서 지수부는 2 + 127 = 129 => 10000001(2)

4. 가수부에는 소수점 아래 값을 그대로 넣어줍니다. 남은 곳은 0으로 채워줍니다. 

따라서 float인 경우, 7.2를 부동 소수점으로 표현하면 아래와 같습니다.

 

7.2 부동 소수점 결과

부동 소수점의 오차

10진수에서 유한 소수가 2진수에서 무한 소수인 경우에, 근사값으로 표현되기 때문에 이로인해 오차가 발생합니다.

 

예를 들어

 

public class FloatingPointTest {
	public static void main(String args[]) {
		System.out.println(2.0 - 1.1);
	}
}

의 결과가 0.9일 거 같지만

2.0 - 1.1 결과

0.899999.. 로 나오게 됩니다. ;(

오차 해결 방법 - BigDecimal 클래스

자바에서 오차를 해결하는 방법은 java.math.BigDecimal 클래스를 이용하는 것입니다.

BigDecimal은 내부적으로 수를 저장할 때 십진수로 저장하여 거의 무한한 정밀도를 보장합니다.

 

따라서 이전 코드와 비교해보면

import java.math.BigDecimal;

public class FloatingPointTest {
	public static void main(String args[]) {
		double a = 2.0;
		double b = 1.1;
		
		BigDecimal a_big = new BigDecimal(String.valueOf(a));
		BigDecimal b_big = new BigDecimal(String.valueOf(b));
		
		System.out.println("BigDecimal 사용 전: " + (a - b));
		System.out.println("BigDecimal 사용 후: " + a_big.subtract(b_big));
	}
}

BigDecimal 클래스

오차없이 결과가 출력됨을 알 수 있습니다.

BigDecimal 클래스에 대한 자세한 설명은 [관련 링크] 이곳을 참고하시면 좋을 것 같습니다 :>

 

잘못된 정보가 있거나, 다른 의견이 있으시다면 피드백 부탁드립니다. 감사합니다 :)