[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. 1.2는 1과 0.2로 나눔.
  2. 1의 2진수 표현: 110 = 12
  3. ​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)는 거짓이 된다.

    그렇다면 소수 연산을 할 때 어떻게 하면 오차가 발생하지 않을 수 있을까?

    정수 변환하여 연산하고 다시 소수로 만들면 된다.