나눗셈과 모듈로 연산의 고찰 - C, C++, Python, Java

시스템 프로그래밍 수업을 듣다 보니, 비트와 관련한 연산 주제들을 많이 다루고 있다. 그런데 수업 중에 유독 이해가 안가는 부분이 있었는데, 나눗셈과 관련된 부분이었다.

C언어에서는 정수 나눗셈을 하면 그 값을 Ceil 시킨다는 것이었다.

이 부분에서 수학적으로 나머지가 항상 0보다 크거나 같다는 생각을 가지고 있었고, 정수 나눗셈이 몫을 구하는 연산이라고 생각하고 있어서 이상하다고 생각했다.

그러나 자세히 알아보니 수학적으로 나머지가 항상 0일 필요는 없다. 더욱이 각 언어마다 나머지를 어떻게 정의하느냐에 따라서 몫과 그 나머지 계산이 다 달라지게 된다.


일단, C언어에서 정수 나눗셈의 나머지는 피제수와 같은 부호를 가지게 된다.(C99 전에는 어느 것을 선택해도 가능했다.)

그리고 나눗셈의 결과는 몫을 나타내게 된다.

예를 들면 아래와 같은 코드는 그 아래와 같은 결과를 나타낸다.
#include <stdio.h>

int main(void)
{
    printf("7 / 3     = %d\n", 7 / 3); 
    printf("7 %% 3     = %d\n", 7 % 3); 
    printf("7 / -3    = %d\n", 7 / -3);
    printf("7 %% -3    = %d\n", 7 % -3);
    printf("-7 / 3    = %d\n", -7 / 3); 
    printf("-7 %% 3    = %d\n", -7 % 3); 
    printf("-7 / -3   = %d\n", -7 / -3);
    printf("-7 %% -3   = %d\n", -7 % -3);
    
    return 0;
}
7 / 3     = 2
7 % 3     = 1
7 / -3    = -2
7 % -3    = 1
-7 / 3    = -2
-7 % 3    = -1
-7 / -3   = 2
-7 % -3   = -1
피제수가 양수일 때 7 / 3 또는 7 / -3은 나머지가 1이 된다. 그러나 -7 / 3 또는 -7 / -3의 나머지는 -1이 된다. 그리고 나눗셈의 결과는 몫을 나타내게 된다.

따라서 나눗셈을 하게 되면 소수의 정수부를 얻는 것과 같은 결과를 얻게 된다.


그렇다면 C++에서는 어떻게 되는지 살펴보자. 코드부터 살펴보면 아래와 같다.
#include <iostream>

using namespace std;

int main(void)
{
    cout<<"7 / 3   = "<<7 / 3<<endl
        <<"7 % 3   = "<<7 % 3<<endl
        <<"7 / -3  = "<<7 / -3<<endl
        <<"7 % -3  = "<<7 % -3<<endl
        <<"-7 / 3  = "<<-7 / 3<<endl
        <<"-7 % 3  = "<<-7 % 3<<endl
        <<"-7 / -3 = "<<-7 / -3<<endl
        <<"-7 % -3 = "<<-7 % -3<<endl;

    return 0;
}
7 / 3   = 2
7 % 3   = 1
7 / -3  = -2
7 % -3  = 1
-7 / 3  = -2
-7 % 3  = -1
-7 / -3 = 2
-7 % -3 = -1
즉, C++ 역시 C언어와 같다는 것을 알 수 있다.


그럼 이번에는 파이썬을 살펴보자.

파이썬의 경우에는 나머지의 부호는 제수의 부호와 같게 된다. 또한, 나눗셈의 결과는 몫을 나타낸다.(파이썬 2의 경우에는 나눗셈만 해도 그 결과는 몫이고, 3의 경우에는 몫 연산자를 사용할 경우 몫이 나온다.)
print('7 // 3     = ', 7 // 3)
print('7 % 3      = ', 7 % 3)
print('7 // -3    = ', 7 // -3) 
print('7 % -3     = ', 7 % -3) 
print('-7 // 3    = ', -7 // 3)
print('-7 % 3     = ', -7 % 3)
print('-7 // -3   = ', -7 // -3) 
print('-7 % -3    = ', -7 % -3) 

print('7.2 // 3   = ', 7.2 // 3)
print('7.2 % 3    = ', 7.2 % 3)
print('7.2 // -3  = ', 7.2 // -3) 
print('7.2 % -3   = ', 7.2 % -3) 
print('-7.2 // 3  = ', -7.2 // 3)
print('-7.2 % 3   = ', -7.2 % 3)
print('-7.2 // -3 = ', -7.2 // -3) 
print('-7.2 % -3  = ', -7.2 % -3)
7 // 3     =  2
7 % 3      =  1
7 // -3    =  -3
7 % -3     =  -2
-7 // 3    =  -3
-7 % 3     =  2
-7 // -3   =  2
-7 % -3    =  -1

7.2 // 3   =  2.0
7.2 % 3    =  1.2
7.2 // -3  =  -3.0
7.2 % -3   =  -1.8
-7.2 // 3  =  -3.0
-7.2 % 3   =  1.8
-7.2 // -3 =  2.0
-7.2 % -3  =  -1.2
이번에는 제수가 양수일 때 나머지의 부호가 양수가 되고, 음수일 때는 부호가 음수가 된다.

따라서 C언어와 비교했을 때 몫이 다르게 나옴을 알 수 있다. 즉, C언어와는 달리 소수의 정수부를 얻지 못한다. 이럴 경우에는 int 함수를 통해서 정수부를 구할 수 있다.

그런데, 파이썬에서는 C나 C++과 달리 소수에 대해서도 몫 연산과 나머지 연산이 가능하다. 자바에서도 마찬가지로 가능한데, 소수이더라도 정수와 같은 규칙이 적용되어 몫과 나머지가 구해진 것을 알 수 있다.


마지막으로 자바를 보자.
public class Divide
{
    public static void main(String[] args)
    {   
        System.out.println("7 / 3     = " + 7 / 3); 
        System.out.println("7 % 3     = " + 7 % 3); 
        System.out.println("7 / -3    = " + 7 / -3);
        System.out.println("7 % -3    = " + 7 % -3);
        System.out.println("-7 / 3    = " + -7 / 3); 
        System.out.println("-7 % 3    = " + -7 % 3); 
        System.out.println("-7 / -3   = " + -7 / -3);
        System.out.println("-7 % -3   = " + -7 % -3);

        System.out.println("7.2 / 3   = " + (int)(7.2 / 3));
        System.out.println("7.2 % 3   = " + 7.2 % 3); 
        System.out.println("7.2 / -3  = " + (int)(7.2 / -3));
        System.out.println("7.2 % -3  = " + 7.2 % -3);
        System.out.println("-7.2 / 3  = " + (int)(-7.2 / 3));
        System.out.println("-7.2 % 3  = " + -7.2 % 3); 
        System.out.println("-7.2 / -3 = " + (int)(-7.2 / -3));
        System.out.println("-7.2 % -3 = " + -7.2 % -3);
    }   
}
7 / 3     = 2
7 % 3     = 1
7 / -3    = -2
7 % -3    = 1
-7 / 3    = -2
-7 % 3    = -1
-7 / -3   = 2
-7 % -3   = -1

7.2 / 3   = 2
7.2 % 3   = 1.2000000000000002
7.2 / -3  = -2
7.2 % -3  = 1.2000000000000002
-7.2 / 3  = -2
-7.2 % 3  = -1.2000000000000002
-7.2 / -3 = 2
-7.2 % -3 = -1.2000000000000002
자바의 경우에는 C나 C++과 같은 결과를 보여준다는 것을 알 수 있다. 즉, 나머지의 부호는 피제수의 부호와 같다. 그리고 나눗셈의 경우 당연히 몫을 나타내고 있다.

그리고 파이썬과 마찬가지로 소수에 대한 나머지 계산이 가능한데, 이 경우에도 위와 같은 규칙으로 나머지가 구해지고 int로 캐스팅 했을 때 위와 같은 몫이 나온다는 것을 알 수 있다.


이런 점에서 나눗셈을 통해서 몫이나 나머지를 구하거나 연산할 때 주의해야 할 필요가 있다


참고 문헌:
http://en.wikipedia.org/wiki/Remainder

댓글 없음:

댓글 쓰기