- 3.3.1 관계 연산자(Relational operators)
- 3.3.2 놓치기 쉬운 잘못된 관계 연산자 사용
- 3.3.3 논리 연산자(Logical operators)
- 3.3.4 놓치기 쉬운 잘못된 논리 연산자 사용
3.3.1 관계 연산자(Relational operators)
관계 연산자는 어려운 개념이 없다. 대소 비교 및 좌우가 같은지 다른지 등을 비교하는 연산자이다. 어떤 표현이 있는지만 잘 알고 있으면 되겠다!
#include <iostream>
int main()
{
using namespace std;
int x, y;
cin >> x >> y;
cout << "Your input values are : " << x << " " << y << endl;
if (x == y)
cout << "equal" << endl;
if (x != y)
cout << "not equal" << endl;
if (x > y)
cout << "x is greater than y" << endl;
if (x < y)
cout << "x is less than y" << endl;
if (x >= y)
cout << "x is greater than y or equal to y" << endl;
if (x <= y)
cout << "x is less than y or equal to y" << endl;
return 0;
}
3.3.2 놓치기 쉬운 잘못된 관계 연산자 사용
이처럼 관계 연산자는 쉽기 때문에 많이 범하는 오류가 있다. 실제로 많은 과학 계산 개발자들이 눈치 채지 못하고 걸려드는 문제이다. 바로 부동소수점수에 대한 비교 연산이다. 부동소수점수에 대한 비교 연산은 정말 애매하다. 굉장히 작은 수에 대한 비교가 이루어져야 하는 경우 컴퓨터에서 어쩔 수 없이 생기는 플로팅 에러가 논리적 연산 결과와는 다른 결과를 낼 수 있기 때문이다. 실제로 디버깅하는 업무를 하다 보면 이 부분에서 생기는 문제들을 정말 많이 접한다.
#include <iostream>
#include <cmath>
int main()
{
using namespace std;
double d1(100 - 99.99); // 0.001
double d2(10 - 9.99); // 0.001
cout << d1 << " " << d2 << endl;
cout << d1 - d2 << endl;
const double epsilon = 1e-10;
if (std::abs(d1 - d2) < epsilon)
cout << "Approximately equal" << endl;
/*if (d1 == d2)
cout << "equal" << endl;*/
else
{
cout << "not equal" << endl;
if (d1 > d2)
cout << "d1 > d2" << endl;
else // d1 < d2 because d1 != d2
cout << "d1 < d2" << endl;
}
return 0;
}
일부러 잘못된 관계 연산자 사용에 대해서는 주석 처리를 하였다. 우리가 생각하기에는 d1과 d2는 같은 수가 나와야 하지만 플로팅 에러의 영향으로 두 수는 미세하게 차이가 난다. 이러한 미세한 차이가 연산을 거듭할수록 불어나기 때문에 항상 주의해야 한다. 따라서 같은 수일지라도 다르게 연산될 수 있기 때문에 입실론이라는 작은 수를 두고 이 수보다 차이가 작은 두 수에 한해서는 근사적으로 같다는 결과로 같음을 표현하기도 한다.
3.3.3 논리 연산자(Logical operators)
논리 연산자 또한 크게 어려운 것은 없지만 추후에 다룰 비트 연산자와 혼동하지 않도록 잘 봐두어야 한다. 논리 연산자의 경우 기호를 쓸 때 두 번 쓴다는 것을 기억해두자! 논리 연산자 자체는 어려울 것이 없으니 아래 코드를 두고 설명은 생략하도록 하겠다!
#include <iostream>
int main()
{
using namespace std;
bool x = true;
bool y = false;
// logical NOT
cout << !x << endl;
// logical AND
cout << (x && y) << endl;
// logicla OR
cout << (x || y) << endl;
// XOR c/c++에는 없는 연산
// false XOR false = false
// false XOR true = true
// true XOR false = true
// true XOR true = false
return 0;
}
3.3.4 놓치기 쉬운 잘못된 논리 연산자 사용
논리 연산자 또한 잘못된 사용을 범할 수 있다. 조건문에서 조건을 논리 연산자로 이어서 작성하는 경우에서 많이 발생한다.
#include <iostream>
int main()
{
using namespace std;
// short circuit evaluation
int x1 = 1; //2로 바꿔서 하면 if의 조건문을 진입안함
int y1 = 2;
// 좌측 조건부터 체크함 하나라도 false가 나오면 그 뒤에 조건은 확인 안함
if (x1 == 1 && y1++ == 2)
{
// do something
}
cout << y1 << endl;
bool v1 = true;
bool v2 = false;
bool v3 = false;
bool r1 = v1 || v2 && v3;
bool r2 = (v1 || v2) && v3;
bool r3 = v1 || (v2 && v3);
cout << r1 << endl;
cout << r2 << endl;
cout << r3 << endl;
return 0;
}
어떠한 프로그램을 설계하던지 계산하는 시간을 줄일 수 있다면 최대한 줄여서 빠르게 작동하게 만드는 것이 일반적이다. 조건문에 대한 판단도 그러한 설계 의도에 따라 불필요한 연산을 하지 않는다. 위의 코드를 복사해서 x1에 대한 값을 바꿔가며 사용해보자. 현재 상태에서는 if문 안으로 진입하게 되면서 y1이 조건문을 통과한 순간 3이 된다. 만약 x1을 바꾼다면 조건문에서 y1++는 수행되지 않고 넘어간다. 불필요한 연산을 피하기 위해서 여러 조건이 and로 연결되어 있는 경우 false라는 조건이 걸리는 순간 그 뒤의 조건은 보지도 않고 지나가기 때문이다. 당연하게 여기는 사람들도 있겠지만 이런 부분에서 버그가 발생하여 의도와는 다르게 작동할 수 있다.
'Programming Language > C++' 카테고리의 다른 글
Section 3.5. 비트 플래그와 비트 마스크 (0) | 2021.11.07 |
---|---|
Section 3.4. 이진수의 보수 개념과 비트단위 연산자 (0) | 2021.11.04 |
Section 3.2. sizeof/comma/조건 연산자 (0) | 2021.11.02 |
Section 3.1. 연산자 우선순위와 산술/증감 연산자 (0) | 2021.11.01 |
Section 2.7. 리터럴 상수와 심볼릭 상수 (0) | 2021.10.19 |