표면적 단순함과 심층적 단순함 : -5/4는 얼마일까
파이썬 마을에 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
(참고로 위에서 mod의 정의는 도날드 크누쓰Donald Knuth가 The Art of Computer Programming V1, 섹션 1.2.4에서 하는 정의와 동등합니다)

여기에서 앞에 언더바가 붙은 숫자는 그 자체로 하나의 명사이며, -는 동사로 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)가 남는다는 것이죠.
by 애자일컨설팅 | 2006/03/22 14:37 | 트랙백(3) | 덧글(6)
트랙백 주소 : http://agile.egloos.com/tb/1666312
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from PRAGMATIC.CO.. at 2006/03/22 16:03

제목 : 제목을 입력해 주세요
전혀 신경쓰지 않던 부분인데 이런 차이가 있었군요....more

Tracked from SoWhat? at 2006/05/09 18:02

제목 : 미처 생각지도 못했던....
미처 생각지도 못했던 지식 매번 감사드리며 트래뷁~ 표면적 단순함과 심층적 단순함 : -5/4는 얼마일까 ...more

Tracked from 진영학, Jin You.. at 2006/10/19 22:54

제목 : 트랙백 : 나머지 연산자에 대해
표면적 단순함과 심층적 단순함 : -5/4는 얼마일까 마지막 부분의 일상생활의 예가 압권입니다. 이해하기 쉬운 책을 쓰기가 가장 어렵다는 쇼펜하우어의 격언이 떠오릅니다. 아주 잘 이해가 갑니다. 엑셀 도움말에서는 mod(number, divisor) 함수를 아래와 같이 설명합니다. "나머지의 부호는 나누는 수의 부호와 같다." 12345ABFormulaDescription (Result)=MO......more

Commented by 퍼키 at 2006/03/22 20:15
역사적인 배경에 대해서 하나 첨언하자면, 음수 나눗셈에 대해서는 초창기에는 별로 정의된 것이 없다가 최초로 표준으로 정의된 곳이 FORTRAN77 이라고 합니다. C 언어에서도 한참동안 표준에서 음수 나눗셈에 대한 뚜렷한 정책이 없어서 컴파일러 벤더마다 다르게 구현을 했다가 90년대에 와서 FORTRAN77을 쓰던 프로그래머들이 짠 옛날 코드들의 호환성과 투표권이 있는 대형 벤더들(FORTRAN방식의 나눗셈을 구현했던)의 입김으로 결국 FORTRAN 방식으로 결정되었다고 하는군요.. 다른 언어에 대해서는 http://caml.inria.fr/pub/ml-archives/caml-list/2001/11/b3c2be254757873bb585152f449853ba.en.html 여기~
자바로써는 C와 비슷한 문법에 대해서 다른 방식을 취하기가 껄끄러웠기 때문에 피할 수 없었을 듯 합니다.
Commented by 3DRicky at 2006/06/04 10:39
머리속이 맑아지네요 감사합니다.
마지막 예는 정말 멋집니다 ^ 0^)b
Commented by soyoja at 2007/09/12 13:20
아주 명쾌한 설명입니다.
감사합니다. ^^
Commented by grizlupo at 2007/11/23 11:57
명쾌한 설명 정말 감사합니다.
정작 답을 구했던 사람의 덧글이 없길래 한 줄 적어 봤습니다. ^^
Commented by 정진철 at 2010/12/23 16:55
요즘 파이썬을 접한 초급자입니다. 궁금한게 있어서요. 파이썬에서 수학의 법칙 중에 결합법칙이 적용됩니까? 실수는 덧셈과 곱셈에 대하여 결합법칙을 만족하는 것으로 압니다만 파이썬에서도 적용되는지 궁금합니다.
Commented by limited10 at 2011/04/14 21:16
마지막 예시만 봐도 이해가 되네요 ^^
명쾌한 설명 감사합니다.

:         :

:

비공개 덧글

< 이전페이지 다음페이지 >


Site Meter