[C#] float, double 연산에서 오차가 생기는 이유
예를 들어보자.
double a = 0.1; double b = 1.1; if (a+b == 1.2) true; else false;
위와 같이 연산할 때 결과는 참일까? 거짓일까?
리턴 값은 false 다.
결론부터 이야기하자면 변수 a와 b에는 입력한 값과 다른 값이 들어가 있기 때문이다.
1.2 이라는 소수를 변수에 입력한다고 할 때 IEEE 754 표준에서는 부호-지수-가수의 형태로 저장된다.
부호는 양수/음수를 구분하고
지수는 정수를
가수는 소수점 이하 부분을 채우게 된다.
이때 채우는 값은 당연히 이진화를 거치는데 이때 문제가 발생한다.
먼저, 1.2를 분해하면 다음 형태로 표현 할 수 있다.
- S: 부호 비트 (S=0S=0, 양수이므로)
- E: 지수 (2의 지수 값)
- M: 가수 (소수점 이하 부분)
- 1.2는 1과 0.2로 나눔.
- 1의 2진수 표현: 110 = 12
- 0.2의 2진수 표현
0.2 × 2 = 0.4 → 정수 부분 0
0.4 × 2 = 0.8 → 정수 부분 0
0.8 × 2 = 1.6 → 정수 부분 1
0.6 × 2 = 1.2 → 반복…
따라서
0.210 = 0.00110011…2 → (순환소수)
가수부분 그러니까 소수를 이진화를 할 때 계속 반복되는 순한소수가 만들어진다.
우리가 수를 저장할 수 있는 공간은 고정이고 무한한 수를 저장할 수 는 없다.
결국 4bit , 8bit , 16bit 같이 허용된 범위 만큼 채우고 잘라서 저장을 하게 된다.
저장된 소수를 다시 10진수로 변환하면 다음과 같이 변환될 것 이다.
1.2 ≈ 1.2000000476837158
정리하자면 우리가 입력한 1.2라는 소수는 프로그램에서는 1.2에 가까운(유사한) 숫자로 저장되게 된다.
따라서 예제의 a + b의 값은 1.2000000000000002 이며
if (a+b == 1.2)는 거짓이 된다.
그렇다면 소수 연산을 할 때 어떻게 하면 오차가 발생하지 않을 수 있을까?
정수 변환하여 연산하고 다시 소수로 만들면 된다.