본문 바로가기
삽질/C, C++, Visual Studio

[ C/C++ 삽질 ] C 혹은 C++에서의 나누기 ("/")

by SteadyForDeep 2021. 2. 2.
반응형

//서론

C/C++ ( 이하 C ) 에서 수치연산을 하다가 보면 너무나도 당연한 부분에서 오류가 쉽게 나곤 한다.

왜냐하면 자료형을 직접 선택해 줘야하는 문제 때문인데

이게 어떨땐 참 편하다가도 어떤 땐 정말 불편한

C가 가진 양날의 검이다.

자료형은 이게 정수형이냐 실수형이냐를 떠나서 좀더 수치해석적으로 사용하는 분야들에서는

혹은 소리와 같이 신호를 현실세계의 아날로그 신호로 복원하는 분야에서는

8, 16, 32, 64 비트중 어떤 것이냐 까지 따지고 들기 때문에 요런 부분을 신경을 잘 써줘야한다.

나누기는 특히나 이 precision(정밀도)이 크게크게 바뀔 수 있는 부분이므로 신경을 써줘야 한다.

 

 

 

 

//몫과 나머지

나누기 라는 연산은 실제로는 하나지만 구해지는 결과는 몫과 나머지 이므로

이 두가지중 어느 것을 구할지 혹은 두가지 모두를 구할지 사용자가 확실하게 알고 있어야 한다.

실재로 많은 언어에서 modulus function이라고 검색하면 몫을 구해주는 연산자를

reamainder function이라고 검색하면 나머지를 구해주는 연산자를 따로따로 알려준다.

for 문이나 if 문 등을 통해서 연속적인 데이터를 처리할때는 이 부분이 필수적이다.

 

하지만..

 

그런 함수를 많이 쓰는건 직관적이지 않고 또 함수의 arguments를 잘못 입력할 경우

공부하고 디버깅을 해야하는 문제가 있다.

그러므로 대부분 "division operator : /"와 "remainder operator operator : %"를 쓴다.

 

 

 

 

//각설

int main()
{
    int a = 3;
    int b = 2;

    int c = a/b;
    std::cout << "= The result of operation ==" << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "= " << c;
    std::cout << "= " << std::endl;
    std::cout << "============================" << std::endl;

}

간단한 결과를 예측해볼 수 있다. 3/2=1.5 인데 반환값을 정수에 넣었으므로 소수점 이하는 버려지고 1만 남는다.

예상대로 1이 나왔다. 그러면 c의 자료형을 float으로 바꾸면 소수점 아래가 나올까?

int main()
{
    int a = 3;
    int b = 2;

    float c = a/b;
    std::cout << "= The result of operation ==" << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "= " << c << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "============================" << std::endl;

}

이번엔 c만 float으로 바꿔서 출력해보자.

그대로다. 우리는 분명 float에 값을 담았는데 왜 소수점 아래가 없는걸까?

그건 바로 division operator의 연산이 끝났을때부터 소수점 아래가 무시되고 출력되었기 때문이다.

int main()
{
    int a = 3;
    float b = 2;

    float c = a/b;
    std::cout << "= The result of operation ==" << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "= " << c << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "============================" << std::endl;

}

위와 같이 b의 자료형을 바꿔보자.

그러면 우리가 원하는 1.5가 나왔다. 즉 division operator는 좌우에 입력받는 값이 어떤 자료형을 가지느냐에 따라

다른 결과값을 출력으로 내는 것이다.

 

 

 

 

사실 이렇게만 보면 좀 어이 없어 보인다. 

그런데 문제는 장문의 코드를 짜면서 발생하는데 예컨데 이런 경우다.

int main()
{
    float c = 3 / 2;
    std::cout << "= The result of operation ==" << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "= " << c << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "============================" << std::endl;

}

이러면 결과가 얼마가 나올까. 당연히 1.5가 나오지 않을까?

놉. 세상에 내 생각대로 당연히 되는건 아무것도 없다. 삼전에 들어가도 손절하고 나오는 세상이다.

실재로 저런 오류가 나면 발견하는데 꽤나 오랜 시간이 걸린다.

C에서는 그냥 1, 2, 3 이렇게 적혀진 숫자들을 int형으로 처리해 버리기 때문에

자칫하면 overflow나 underflow등의 문제에 직면할 수 있다.

 

아니 그러면 저 간단한 수식 하나 써먹을려고 모든 수치를 일일이 float a; float b; 해야한다는 말인가?

int main()
{
    float c = 3 / 2.;
    std::cout << "= The result of operation ==" << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "= " << c << std::endl;
    std::cout << "= " << std::endl;
    std::cout << "============================" << std::endl;

}

뭐가 바뀌었는지 찾았는가?

틀린그림찾기의 답은 2뒤에 점이 찍혀있다는 것이다. 이 점은 소수점 아래에 0이 생략되어있다는 의미로

아래와 같이 달라진다.

 

1  == (int)1

1. == (double)1.0

 

그리고 이쯤에서 눈치챘겠지만 division operator는 분모의 자료형을 따라가기 때문에

2 뒤에 점을 찍어주는 것 만으로도 충분히 우리가 원하는 수치를 얻을 수 있다.

 

또 다른 사람이 만든 API 때문에 int를 분모에 사용해야만 한다면

선언된 변수 앞에 (float)혹은 (double)을 붙여서 형변환을 해주는 것으로 쉽게 문제를 해결할 수 있다.

 

 

 

 

반응형

댓글