3장 코딩과 디버깅에 관하여 3

Updated:
2 minute read

3.6 실수 자료형의 이해

부동 소수점 표기

자료형 부호 비트 지수 비트 가수 비트 지수 범위 유효자리수(십진수)
32비트 실수형 1 8 23 -27+2 ~ 27-1 6
64비트 실수형 1 11 52 -210+2 ~ 210-1 15
80비트 실수형 1 15 64 -214+2 ~ 214-1 18

float 형식

실수 비교하기

int same = 0;
for(int x = 1; x <= 50; ++x) {
    double y = 1.0 / x;
    if(x * y == 1.0)
        ++same;
}

위 코드를 실행해보면 same의 값은 50보다 적다.

실수 1/x가 근사값이 되어서 x와 곱해도 1이 안 되기 때문이다.

이를 보완하기 위해 비교문을 다음과 같이 수정하였다.

bool absoluteEqual(double a, double b) {
    return fabs(a - b) < 1e-10;
}
// fabs()는 부동 소수점 인수의 절대값을 계산한다.
int same = 0;
for(int x = 1; x <= 50; ++x) {
    double y = 1.0 / x;
    if(absoluteEqual(1, x * y))
        ++same;
}

하지만 수가 굉장히 커져서 1-10의 오차범위를 벗어난다면 수정된 코드 또한 잘못된 결과를 낳을 수 있다.

이를 해결하기 위해 크게 두 가지 방법을 사용한다.

  • 입력될 값의 범위를 예측하여 직접 오차 범위를 설정한다.
  • 입력될 값의 크기에 비례해서 오차 범위를 설정한다.

비교할 실수의 크기들에 비례한 오차 한도를 정한다.

입력의 최대치와 최소치를 예측할 수 있다면 오차 한도의 상한과 하한을 구해서 적용한다.

상대 오차를 이용한다.

// a와 b의 오차가 더 큰 수의 0.000001% 이하이면 true를 반환한다.
bool relativeEqual(double a, double b) {
    return fabs(a - b) <= 1e-8 * max(fabs(a), fabs(b));
}

하지만 이 방법은 max(fabs(1), fabs(1e-12)) = 1처럼 1-8보다 더 작은 차이가 나는 수들이 같은지 확인 하는 데에는 부적절하다.

그래서 두 수의 절대 오차가 매우 작을 경우에 두 수가 같다고 판단하는 조건문을 추가한다.

// 절대 오차와 상대 오차를 모두 이용해서 두 수가 같은지 판정한다.
bool doubleEqual(double a, double, b) {
    double diff = fabs(a) - fabs(b);
    // 절대 오차가 허용 범위 안일 경우 무조건 true를 반환한다.
    if(diff < 1e-10) return true;
    // 이 외의 경우에는 상대 오차를 사용한다.
    return diff <= 1e-8 * max(fabs(a), fabs(b));
}

대소 비교

두 수가 같은지 판단하는 것이 아닌 대소 비교에서도 오차로 인한 오답을 피하기 위해서 비교하는 두 값이 아주 가까운 경우를 먼저 확인하고 처리해야 한다.

실수 연산 아예 하지 않기

  • 곱셈과 나눗셈의 순서를 바꾸기
  • 양변 제곱하기
  • 실수 좌표를 써야 하는 기하 문제에서 좌표계를 가로 세로로 정수배 늘리기
Back to top ↑

Leave a comment