Skip to content

77. 실수 자료형의 오차


1. 키워드

  • 부동 소수점(Floating Point)
  • 머신 엡실론(Machine Epsilon)


2. 실수 자료형의 오차

  • 부동 소수점 방식은 실수를 정확히 표현할 수 없는 문제가 있다.


#include <stdio.h>

int main()
{
    float num1 = 0.0f;
    float num2 = 0.1f;

    // 0.1을 10번 더함
    for (int i = 0; i < 10; i++)
    {
        num1 = num1 + num2;
    }

    printf("%.15f\n", num1); // 1.0이 나와야 하지만 반올림 오차 발생

    return 0;
}

// 1.000000119209290


  • 분명 0.110번 더했으므로 1.0이 나와야 하는데 1.000000119209290이 나왔다.
  • 왜냐하면 수학적으로 실수는 무한히 많은데 이 실수를 유한 개의 비트로 표현하기 위해서는 근삿값으로 표현해야 하기 때문이다.
  • 이런 문제를 부동 소수점 반올림 오차(Rounding Error)라고 한다.
  • printf에서 소수의 자릿수를 좀 더 많이 표시하고 싶다면 %.15f처럼 서식 지정자에 자릿수를 지정해 주면 된다.


  • 다음과 같이 실수는 연산한 값을 ==으로 직접 비교하면 안 된다.


#include <stdio.h>

int main()
{
    float num1 = 0.0f;
    float num2 = 0.1f;

    // 0.1을 10번 더함
    for (int i = 0; i < 10; i++)
    {
        num1 = num1 + num2;
    }

    // num1: 0.100000001490116
    if (num1 == 1.0f) // 반올림 오차가 발생하므로 실수는 ==로 비교하면 안됨
        printf("true\n");
    else
        printf("false\n"); // num1은 1.0이 아니므로 false 출력

    return 0;
}

// false


  • 이미 연산한 결과에서 반올림 오차가 발생해버렸기 때문에 ==로 직접 비교하면 잘못된 결과가 나오게 된다.


  • 오차를 감안하여 실수를 비교하려면 다음과 같이 FLT_EPSILON을 사용해야 한다.


#include <stdio.h>
#include <float.h> // float 타입의 머신 엡실론 값 FLT_EPSILON이 정의된 헤더 파일
#include <math.h>  // float 타입의 절댓값을 구하는 fabsf 함수를 위한 헤더 파일

int main()
{
    float num1 = 0.0f;
    float num2 = 0.1f;

    // 0.1을 10번 더함
    for (int i = 0; i < 10; i++)
    {
        num1 = num1 + num2;
    }

    // num1: 1.000000119209290
    if (fabsf(num1 - 1.0f) <= FLT_EPSILON) // 연산한 값과 비교할 값의 차이를 구하고 절댓값으로
                                           // 만든 뒤 FLT_EPSILON보다 작거나 같은지 판단
                                           // 오차가 머신 엡실론 이하라면 같은 값으로 봄

        printf("true\n"); // 값의 차이가 머신 엡실론보다 작거나 같으므로 true
    else
        printf("false\n");

    return 0;
}

// true


  • 먼저 연산한 값과 비교할 값의 차이를 구한 뒤 FLT_EPSILON보다 작거나 같은지 판단한다.
  • 값의 차이는 math.h 헤더 파일의 fabsf 함수를 사용하여 절댓값으로 만들면 음수가 나오더라도 정상적으로 판단할 수 있다.
  • FLT_EPSILONfloat.h 헤더 파일에 정의되어 있으며 이 값을 머신 엡실론이라 부른다.
  • 어떤 실수를 가장 가까운 부동소수점 실수로 반올림했을 때 상대 오차는 항상 머신 엡실론 이하가 된다.
  • 즉, 머신 엡실론은 반올림 오차의 상한값이며 연산한 값과 비교할 값의 차이가 머신 엡실론보다 작거나 같다면 두 실수는 같은 값이라 할 수 있다.
  • 만약 double, long double 타입을 사용한다면 머신 엡실론은 DBL_EPSILON, LDBL_EPSILON을 사용한다.

References