최초에 주어진 함수는 다음과 같다.

int32_t add2(int32_t x, int32_t y) {
    if(x + y > INT32_MAX) {
        throw std::invalid_argument("Overflow!!");
    } else {
        return (x + y);
    }
}

이에 대한 개선된 함수는 다음과 같다고 한다. 그러나 여전히 문제가 있다.

int32_t add2(int32_t x, int32_t y) {
    if(x + y > INT32_MAX || x + y < INT32_MIN) {
        throw std::invalid_argument("Overflow!!");
    } else {
        return (x + y);
    }
}

위 코드에서 문제가 되는 점

x + y가 int32_t 데이터 타입이 허용하는 범위를 벗어나면 이미 오버플로우가 발생한다. 예를 들어 x가 2,147,483,647, y가 1이면 x + y은 이미 오버플로우가 발생하여 -2,147,483,648이 되어있기 때문에 위의 로직으로는 걸러지지가 않는다.

따라서

  • x, y의 합이 오버플로우가 발생하는 로직을 개선한 함수를 구현하고,
  • 추가적으로 x, y의 차에 대한 함수도 구현하기.

1. 최초에 제출한 코드

#include <iostream>
#include <cstdint>

using namespace std;

int32_t add(int32_t, int32_t);
int32_t sub(int32_t, int32_t);


int main() {
    int32_t x = -2000000000;
    int32_t y = 2000000000;

    try {
        cout << sub(x, y) << endl;
    } catch(invalid_argument& ia) {
        cout << ia.what() << endl;
    }

    return 0;
}

int32_t add(int32_t x, int32_t y) {
    if(x >= 0) {
        if(y > (INT32_MAX - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위를 넘었습니다.");
        }
    } else {
        if(y < (INT32_MIN - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위보다 작습니다.");
        }
    }
    return x + y;
}

int32_t sub(int32_t x, int32_t y) {
    if(x >= 0) {
        if(y < (INT32_MAX - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위를 넘었습니다.");
        }
    } else {
        if(y > (INT32_MIN - x)) {
            throw invalid_argument("Overflow: 두 수의 합이 허용하는 범위보다 작습니다.");
        }
    }
    return x - y;
}

2. 그럼 위의 코드 실행은 정상적으로 동작하는가?

sub2 함수 실행시 문제가 있었음. 기본적으로 다음과 같은 값 갖고 테스트를 해봤을 때

x = 0, y = 2,147,483,646, 기대값 = -2,147,483,646
x = 0, y = 0, 기대값 = 0
x = 2, y = 0, 기대값 = 2

모든 경우에서 발생해서는 안되는 에러가 발생함.

x 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). 2
y 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). 0
입력받은 x값: 2
입력받은 y값: 0
x - y = Overflow: 두 수의 합이 허용하는 범위를 넘었습니다.

add2 함수를 구현한 후, 단순히 부호만 뒤집어주면 sub2 함수가 실행이 될 것이라고 생각을 했는데, 이 부분에 대해 아예 잘못 생각하고 테스트도 하지 않고 넘어가서 전혀 엉뚱하게 작동함. 따라서 새로 로직을 다음과 같이 구성함.

(1) 두 변수의 차를 구할 때 오버플로우가 발생할 가능성이 있는 경우

0도 양수로 포함

  1. x가 음수, y가 양수인 경우
  2. x가 양수, y가 음수인 경우

1번 경우에서 x - y를 하는 경우, 음수 오버플로우가 발생하여 연산 값이 양으로 나오게 되고, 2번 경우에서는 양수 오버플로우가 발생하여 음의 경우가 나오게 됩니다. 따라서 이 두 부분을 걸러내야 함.

(2) 위의 코드 자체의 문제점

쓸데없이 if문 안에 if문을 들여서 가독성을 떨어뜨리는 문제

두가지를 고려했을 때 수정한 코드는 다음과 같음.

int32_t sub2(int32_t x, int32_t y) {

    // 두 변수의 차를 계산하여 담을 변수
    int32_t subResult = x - y;

    // x가 음수, y가 양수인 경우에 subResult가 양수인 경우
    if(x < 0 && y > 0 && subResult > 0) {
        throw invalid_argument("negative overflow");
    } 

    // x가 양수, y가 음수인 경우에 subResult가 음수인 경우
    else if(x > 0 && y < 0 && subResult < 0) {
        throw invalid_argument("positive overflow");
    }

    return subResult;
}

한편, add2 함수 또한 가독성 측면에서 문제가 있어서 새로이 로직을 구성하기로 함.

0도 양수로 포함

  1. x가 양수인데 y도 양수인 경우 둘의 합이 음수이면 positive overflow
  2. x가 음수인데 y도 음수인 경우 둘의 합이 양수이면 negative overflow
int32_t add2(int32_t x, int32_t y) {
    // 두 변수의 합을 계산하여 담을 변수
    int32_t addResult = x + y;

    // x 및 y가 양수인데 그 합이 음수인 경우 positive overflow 예외처리
    if(x >= 0 && y >= 0 && addResult < 0) {
        throw invalid_argument("positive overflow");
    }

    // x 및 y가 음수인데 그 합이 양수인 경우 negative overflow 예외처리
    else if(x < 0 && y < 0 && addResult >= 0) {
        throw invalid_argument("negative overflow");
    }

    return addResult;
}

3. 재구성한 코드에 대한 테스트

위에서 재구성한 코드 전체 내용은 다음과 같음.

#include <iostream>
#include <cstdint>

using namespace std;

int32_t add2(int32_t, int32_t);
int32_t sub2(int32_t, int32_t);


int main() {
    // x, y의 입력값은 각각 정상범위 (-2,147,483,648 ~ 2,147,483,647)라고 가정.
    int32_t x;
    int32_t y;

    cout << "x 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). ";
    cin >> x;

    cout << "y 값을 입력하세요 (허용범위: -2,147,483,648 ~ 2,147,483,647). ";
    cin >> y; 

    try {
        cout << "입력받은 x값: " << x << endl;
        cout << "입력받은 y값: " << y << endl;
         cout << "x + y = " << add2(x, y) << endl;   
        cout << "x - y = " << sub2(x, y) << endl;
    } catch(invalid_argument& ia) {
        cout << ia.what() << endl;
    }

    return 0;
}

int32_t add2(int32_t x, int32_t y) {
    // 두 변수의 합을 계산하여 담을 변수
    int32_t addResult = x + y;

    // x 및 y가 양수인데 그 합이 음수인 경우 positive overflow 예외처리
    if(x >= 0 && y >= 0 && addResult < 0) {
        throw invalid_argument("positive overflow");
    }

    // x 및 y가 음수인데 그 합이 양수인 경우 negative overflow 예외처리
    else if(x < 0 && y < 0 && addResult >= 0) {
        throw invalid_argument("negative overflow");
    }

    return addResult;
}

int32_t sub2(int32_t x, int32_t y) {

    // 두 변수의 차를 계산하여 담을 변수
    int32_t subResult = x - y;

    // x가 음수, y가 양수인 경우에 subResult가 양수인 경우
    if(x < 0 && y >= 0 && subResult > 0) {
        throw invalid_argument("negative overflow");
    } 

    // x가 양수, y가 음수인 경우에 subResult가 음수인 경우
    else if(x >= 0 && y < 0 && subResult < 0) {
        throw invalid_argument("positive overflow");
    }

    return subResult;
}

다음 변수를 입력하여 각각 테스트를 진행한 결과

x y add2 연산 기대값 add2 실제 결과값 sub2 연산 기대값 sub2 실제 결과값
0 0 0 0 0 0
0 1 1 1 -1 -1
1 0 1 1 1 1
1 1 2 2 0 0
2,147,483,647 -2,147,483,648 -1 -1 positive overflow positive overflow
-2,147,483,648 2,147,483,647 -1 -1 negative overflow negative overflow
1 2,147,483,647 positive overflow positive overflow -2,147,483,646 -2,147,483,646
2,147,483,647 1 positive overflow positive overflow 2,147,483,646 2,147,483,616
0 -2,147,483,648 -2,147,483,648 -2,147,483,648 positive overflow positive overflow
-1 -2,147,483,648 negative overflow negative overflow 2,147,483,647 2,147,483,647
2,147,483,647 2,147,483,647 positive overflow positive overflow 0 0
-2,147,483,648 -2,147,483,648 negative overflow negative overflow 0 0

4. 수행 중 배운 점

언더플로우와 오버플로우 개념의 혼동

기존에는 특정 데이터타입의 최대값보다 큰 값이 주어진 것을 오버플로우, 최소값보다 작은 값이 주어진 것을 언더플로우라고 착각하고 있었다.

  • 오버플로우: 특정 데이터타입이 담을 수 있는 범위를 넘어선 값이 주어진 경우
  • 언더플로우: 부동소수점 연산시에 precision 허용 범위보다 더 적은 값을 표현하지 못하는 경우

5. 이어서 수행할 과제

예외처리를 하지 않고 처리하는 방법을 적용하여 코드 개선하기

반응형

+ Recent posts