2013년 8월 27일 화요일

C 언어에서 나머지 연산자 % 에 대하여

우리가 통상 사용하고 있는 나머지 연산자 (%) 에 대한 짧은 이야기를 해 보겠다.

나머지 연산자는 얼핏 보면 나눗셈 연산자 (/) 와 더불어 정수의 몫과 나머지를 구하는
데에 사용되기도 한다. 그런데 다음의 경우를 비교해 보자.

[예 1]  5 /  2
[예 2] -5 / -2
[예 3]  5 / -2
[예 4] -5 /  2

[예 1]의 답은 당연히 2 이다. 정수에서는 소수점 이하는 버리기 때문이다. 또한 [예 2], [예 3] [예 4] 의 답도 각각 2, -2, -2 이다. (초등학교때 배웠던 양수와 음수의 곱셈과 나눗셈 법칙과 같다.)

그렇다면 다음의 예는 어떨까?

[예 5]  5 %  2
[예 6] -5 % -2
[예 7]  5 % -2
[예 8] -5 %  2

실제로 결과를 보면 알겠지만 결과는 각각 1, -1, 1, -1 로 계산되어 나온다. 이렇게 나오는 이유를 설명하겠다.

C 언어의 % 연산자에서 음수 처리는 다음과 같이 정의되어 있다.
X % Y 를 연산할 때 그 연산 결과의 부호는 X 의 부호를 따라간다. 즉 Y의 부호는 무시되며 오직 X가 양수이면 결과는 양수, 음수이면 결과는 음수가 된다. 

쉽게 말하면 [예 5] ~ [예 8]의 결과는, 일단 부호가 모두 양수라 가정하고 계산을 한 후 앞의 수의 부호를 붙인다는 뜻이다.

여기서 한 가지 고찰을 해 보자. 얼핏 보기에 저렇게 정의되는 나머지 연산이 별 것 아니라 느껴지겠지만, 우리가 나머지 연산을 사용하여 구현하는 것 중 일정 주기의 offset 을 구하는 등의 다음과 같은 경우가 있을 수 있겠다. 예를 들어 앞의 수가 차례로 증가 혹은 감소할 때마다 0, 1, 2, 3 의 번호를 주기적으로 부여하는 경우이다.

...
int i;
for( i=100; i>0; i-- )
{
    printf("번호 %d 는 %d 조 입니다.\n", i, i%4);
}
...

기대하는 것은 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, ... 식으로 주기적으로 조를 정해주는 것인데, 만일 i 가 0보다 작은 음수로 들어가게 되면, 즉

...
int i;
for( i=50; i>=-50; i-- )
{
    printf("번호 %d 는 %d 조 입니다.\n", i, i%4);
}
...

이런 경우에는 양수에서 음수로 바뀌는 부분에서 기대하는 것 처럼 조를 정해주지 않는다. 
즉, 결과가 다음과 같이 나오게 된다. 

... 3, 2, 1, 0, 3, 2, 1, 0, -1, -2, -3, 0, -1, -2, -3 ...

이런 경우는, 결과가 음수일 경우 4를 더하는 등의 방법 등으로써 해결하는 구문이 추가로
요구된다.

^^


즉, 요점은 이렇다.
연산자 자체의 기능만을 설명한 것을 본다면 어렵지 않고 별로 중요하지 않은 것처럼 보이지만, 실제로 이를 응용하여 실전에서 사용하게 될 경우에는 자신도 몰랐던 예상치 못한 결과가 나올 수도 있다는 것을 항상 염두해야 한다는 것이다.

이것이 올바르고 효과적인 디버깅 자세의 시작이 아닐까 한다.

-----------------------

또하나 (중요한 이야기를) 추가하면...

나머지 연산은 실수형에선 동작하지 않는다는 것이다. 즉

  5 % 2

의 값은 1 로 제대로 나오지만

  5.0 % 2.0
  5 % 2.0
  5.0 % 2

등은 컴파일이 되지 않는다는 것을 추가로 알아두자.


댓글 2개:

  1. 찾아보고있엇는데 친절한 설명 감사합니다!

    답글삭제
  2. Casinos Near Casinos and Casinos NEAR ME - Mapyro
    Casinos near Casinos and 부산광역 출장마사지 Casinos NEAR ME · Hotels Near 경상남도 출장샵 Casinos and 나주 출장안마 Casinos · Best Western 공주 출장안마 Casino Resort in East 강원도 출장마사지 Anglia · New Lake Casino & Spa · Santa

    답글삭제