궁금하던 차에 찾은 글.. 원문은 링크를 참조하세요.
파이썬 마을에 grizlupo님이
글을 하나 올리셨습니다. 파이썬이라는 언어에서는 -5/4가 -2인 반면, 다른 언어(특히, C, 자바 등 주류언어)에서는 그 결과가 -1인데 여기에 대해 커멘트를 구한다는 내용입니다.
정수 나눗셈 방식을 정하면 나머지(remainder) 연산도 자동으로 정해지게 됩니다. 몫 곱하기 나누는 수(제수) 더하기 나머지 하면 무조건 나뉘는 수(피제수)가 되는 원칙에 맞추려고 하기 때문입니다. 즉, -5/4를 -2로 보면, -5 나누기 4의 나머지는 3이어야 하고, -5/4를 -1로 보면, 나머지는 -1로 보아야 합니다(많은 수학자들은 제수가 양수인데 나머지가 음수가 될 수 있다는 것에 알레르기적 반응을 보이는 것 같습니다).
둘 중에 어느 방식이 옳을까요? 이 질문은 사실 답할 수가 없습니다. 수학이란 정의와 약속 위에 건축된 궁전이기 때문에 어떤 정의에 대해 옳고 그르다를 말하기가 어렵습니다. 하지만 어느 방식이 더 유용하거나 혹은 "엘레강트"할까요 하는 질문은 할 수 있습니다.
제가 여기에 대해 이렇게 커멘트를 달았습니다.
수학에서 Negate와 Negative Sign의 혼용에서 오는 문제점 같습니다. 연산자 우선순위가 있는 언어에서는 우선순위 문제(혹은 수학적 연산 우선순위의 해석 문제)라고 볼 수도 있겠죠.
즉, -5/4를 (-5)/4로 보냐, -(5/4)로 보냐의 문제인데... 자바 경우, 괄호에 상관없이 무조건 후자로 생각하죠 -- 즉 음수를 나누는 것도 결국 어떤 (물론 음의) 量 amount을 나누는 것으로 보는 겁니다. 그러면 음수를 어떻게 나눠야 하는지 고민하지 않아도 될 것 같습니다만, 정말 이게 더 간단하거나 유용할까요?
J라는 언어에서는 연산자 우선순위가 없고, negate와 negative sign을 따로 구분해서 이 문제를 해결합니다. 나누기와 floor라는 수학적 개념만으로 정수 나누기와 나머지 연산을 정의해 봅시다.
코드: |
floor=. <. of=. @ div=. % intdiv=. floor of div _5 intdiv 4 _2 - 5 intdiv 4 _1 left=.[ right=.] mod=. left - right * intdiv _5 mod 4 3 - 5 mod 4 _1 |
여기에서 앞에 언더바가 붙은 숫자는 그 자체로 하나의 명사이며, -는 동사로 negate라는 이름을 갖습니다. 즉, 음수라는 개념이 독립적으로 따로 있는 것이죠. J에서는 기본적으로 연산자가 무진장 많기 때문에 우선순위가 없고 대신 무조건 오른쪽에서 왼쪽으로 계산이 진행됩니다. 그리고 연산순서를 바꾸고 싶으면 괄호를 사용합니다.
저는 파이썬의 방식, 즉,
코드: |
>>> -(5/4) -1 >>> (-5)/4 -2 |
로 되는 것이, 자바의 방식(두 경우 모두 -1이 되는)에 비해, 훨씬 더 일관성 있고 간단한 방식이라고 생각합니다(위에 나온 intdiv나 mod의 정의를 보세요).
일견 느끼기에는 모든 경우에 -1로 나오는 자바 방식이 쉽고 간단한 것 같지만 절대 그렇지 않습니다. 표면적 단순함에 속아서는 안됩니다.
참고를 위해 자바의 나눗셈 연산 설명을 아래에 인용합니다.
인용: |
The binary / operator performs division, producing the quotient of its operands. The left-hand operand is the dividend and the right-hand operand is the divisor. Integer division rounds toward 0. That is, the quotient produced for operands n and d that are integers after binary numeric promotion (§5.6.2) is an integer value q whose magnitude is as large as possible while satisfying |d * q | <= |n|; moreover, q is positive when |n| >= |d| and n and d have the same sign, but q is negative when |n| >= |d| and n and d have opposite signs. http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5047 |
혹시나 해서 한가지 예를 덧붙이자면,
코드: |
range=.10 1000 NB. 좌표축에서 10부터 1000까지 선분이 있다고 할 때 range intdiv 4 NB. 1/4로 사이즈를 축소하면 2 250 range-100 NB. 원래 선분을 좌로 100만큼 움직이면 _90 900 (range-100) intdiv 4 NB. 이동한 선분을 1/4로 축소하면 _23 225 diff=. |@-/ diff range intdiv 4 NB. 원래 축소한 선분의 길이 248 diff (range-100) intdiv 4 NB. 좌측으로 이동한 선분을 축소한 길이 248 |
하지만 자바에서는 마지막 결과가 247로 나옵니다. 0을 중심으로 나눗셈 연산이 비대칭적이기 때문입니다.
이런 이유로 제가 자바(및 C 등등)의 나눗셈 연산이 더 복잡하면서 일관성이 떨어진다고 하는 것입니다.
--김창준
p.s.
일상생활의 예를 추가합니다.
14 나누기 3을 이렇게 해석해 봅시다. 우선 세상에 만원짜리 돈만 있다고 가정합시다.
상금으로 14만원을 받았습니다. 사람은 셋입니다. 14를 3으로 나누면 몫이 4가 되고 나머지가 2가 됩니다. 무슨 이야기냐면 각자 4만원씩 갖고 가고 2만원이 수중에 남는다는 겁니다. 같이 술이라도 한 잔 마시러 가면 되겠네요.
이제는 -14 나누기 3을 생각해 보죠. 술집에서 계산을 했더니 14만원이 나왔습니다. 사람은 셋입니다. 얼마씩 내야할까요. 4만원씩 내면 돈이 모자라죠. 5만원씩 내면 일단 술값을 낼 수는 있네요. 대신 1만원이 남습니다. 빚으로 1만원이 남는 것이 아니고, 우리에게 1만원이 더 있는 것이죠. 역시 그 만원으로 어디 가서 음료수라도 마시면 되겠네요.
자 이제 생각해 봅시다. 돈을 내는 것, 모자라는 것은 음수입니다. 돈을 받는 것, 남는 것은 양수입니다. 14만원을 내야하는 데(-14) 세 명이 나누면(-14 / 3) 한 사람당 담당할 몫은 "5만원씩 내는 것(-5)"입니다. 그러고 나면 1만원이 남습니다.
이 방식이 파이썬의 방식이고, 자바 방식은 한 사람당 4만원씩 내고(-4), 그 결과 2만원의 부채(-2)가 남는다는 것이죠.
최근 댓글 목록