소년코딩

03.05 - 관계 연산자 (Relational operators)

C++ 에는 여섯 가지 관계 연산자(relational operator) (or 비교 연산자)가 있다.

Operator Symbol Form Operation
Greater than > x > y true if x is greater than y, false otherwise
Less than < x < y true if x is less than y, false otherwise
Greater than or equals >= x >= y true if x is greater than or equal to y, false otherwise
Less than or equals <= x <= y true if x is less than or equal to y, false otherwise
Equality == x == y true if x equals y, false otherwise
Inequality != x != y true if x does not equal y, false otherwise

위 연산자들은 상당히 직관적인 기호다. 각 연산자눈 부울 값(bool value) true(1) 또는 false(0)를 반환한다.

위 연산자들은 정수 비교 시 간단하게 사용할 수 있다:
#include <iostream>

int main()
{
    std::cout << "Enter an integer: ";
    int x;
    std::cin >> x;

    std::cout << "Enter another integer: ";
    int y;
    std::cin >> y;

    if (x == y)
        std::cout << x << " equals " << y << "\n";
    if (x != y)
        std::cout << x << " does not equal " << y << "\n";
    if (x > y)
        std::cout << x << " is greater than " << y << "\n";
    if (x < y)
        std::cout << x << " is less than " << y << "\n";
    if (x >= y)
        std::cout << x << " is greater than or equal to " << y << "\n";
    if (x <= y)
        std::cout << x << " is less than or equal to " << y << "\n";

    return 0;
}

And the results from a sample run:

Enter an integer: 4
Enter another integer: 5
4 does not equal 5
4 is less than 5
4 is less than or equal to 5

부동 소수점 숫자 비교 (Comparison of floating point values)

관계 연산자를 사용해서 부동 소수점 숫자를 비교하는 것은 위험하다. 부동 소수점 숫자의 반올림 오류 때문에 예상치 못한 결과가 나타날 수 있기 때문이다.

오류의 예:
#include <iostream>

int main()
{
    double d1(100 - 99.99); // should equal 0.01
    double d2(10 - 9.99);   // should equal 0.01

    if (d1 == d2)
        std::cout << "d1 == d2" << "\n";
    else if (d1 > d2)
        std::cout << "d1 > d2" << "\n";
    else if (d1 < d2)
        std::cout << "d1 < d2" << "\n";

    return 0;
}

위 프로그램은 예상치 못한 결과를 출력한다.

d1 > d2

위 프로그램은 d1 = 0.0100000000000005116, d2 = 0.0099999999999997868 이다. 두 숫자는 0.01에 가깝지만, dd2 보다 크다.

부동 소수점 숫자에 연사자 == 또는 != 를 사용하는 것은 권장하지 않는다. 부동 소수점 숫자가 같은지 평가하는 가장 일반적인 방법은 두 값이 얼마나 가까운 지 계산하는 함수를 만드는 것이다. 두 숫자가 "충분히 가깝다면" 같다고 평가한다. "충분히 가깝다"를 평가하는데 사용하는 값은 전통적으로 epsilon이라고 한다. epsilon은 일반적으로 소수(Ex. 0.0000001)로 정의한다.

보통 다음과 같이 함수를 작성한다:
#include <cmath> // for fabs()
bool isAlmostEqual(double a, double b, double epsilon)
{
    // a와 b 사이의 값이 epsilon보다 작으면 "충분히 가깝다".
    return fabs(a - b) <= epsilon;
}

<cmath> 라이브러리의 fabs() 함수는 매개 변수의 절댓값을 반환하는 함수다.

그러나 위 함수는 효율적이지 못하다. 0.00001 epsilon1.0에는 적합하지만, 0.0000001에는 너무 크고 10,000과 같은 숫자에는 너무 작다. 이것은 함수를 호출할 때마다 입력에 적합한 epsilon을 선택해야 한다는 걸 의미한다.

유명한 컴퓨터 과학자인 Donald Knuth는 다음과 같은 방법을 사용했다.

#include <cmath>

// a와 b의 차이가 더 작은 수 * epsilon보다 같거나 작으면 true를 반환한다.
bool approximatelyEqual(double a, double b, double epsilon)
{
    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

int main()
{
    if (approximatelyEqual(a, b, 0.001))
        std::cout << a << " is equals " << b << "\n";

    return 0;
}

위 함수가 어떻게 동작하는지 알아보자. 위 함수에서 "충분히 가깝다"는 a와 b중 큰 것의 1%내에 있다는 것을 의미한다.

approximatelyEqual() 함수는 잘 작동하지만, 숫자가 0에 가까워질수록 완벽하지는 않다.

#include <iostream>
int main()
{
    // a is really close to 1.0, but has rounding errors, so it's slightly smaller than 1.0
    double a = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;

    // First, let's compare a (almost 1.0) to 1.0.
    std::cout << approximatelyEqual(a, 1.0, 1e-8) << "\n";

    // Second, let's compare a-1.0 (almost 0.0) to 0.0
    std::cout << approximatelyEqual(a-1.0, 0.0, 1e-8) << "\n";
}

Perhaps surprisingly, this returns:

1
0

위 프로그램에서 두 번째 코드가 예상대로 수행되지 않는다. 이같은 문제를 피하는 한 가지 방법은 첫 번째 방식의 absolute epsilon과 Donald Knuth 방식의 relative epsilon을 사용하는 것이다.

// return true if the difference between a and b is less than absEpsilon, or within relEpsilon percent of the larger of a and b
bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon)
{
    // Check if the numbers are really close -- needed when comparing numbers near zero.
    double diff = fabs(a - b);
    if (diff <= absEpsilon)
        return true;

    // Otherwise fall back to Knuth's algorithm
    return diff <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * relEpsilon);
}

위 알고리즘은 새로운 매개 변수 absEpsilon을 추가했다. 먼저 ab 사이의 거리가 absEpsilon 보다 작은지 확인하다. 이 값은 매우 작은 값(Ex. e0-12)으로 설정해야 한다. 이런 ab가 모두 0에 가까운 경우를 처리한다. 그 다음 Donald Knuth의 알고리즘으로 돌아간다.

#include <iostream>

int main()
{
    // a is really close to 1.0, but has rounding errors
    double a = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;

    std::cout << approximatelyEqual(a, 1.0, 1e-8) << "\n";     // compare "almost 1.0" to 1.0
    std::cout << approximatelyEqual(a-1.0, 0.0, 1e-8) << "\n"; // compare "almost 0.0" to 0.0
    std::cout << approximatelyEqualAbsRel(a-1.0, 0.0, 1e-12, 1e-8) << "\n"; // compare "almost 0.0" to 0.0
}

1
0
1

approximatelyEqualAbsRel()은 작은 입력값도 올바르게 처리한다


cpp 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/35-relational-operators-comparisons/ 입니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기