대부분의 프로그래밍 언어에서는 사칙연산을 포함한 기본적인 산술 기능이나 고급 수학 함수들을 제공합니다. 그리고 여기에 올림이나 내림, 반올림 같은 정밀도와 관련된 기능도 포함됩니다. 숫자를 포맷팅하는 기능을 제공하는 언어는 어떤 숫자값을 소수점 아래 몇 자리까지 표현하는 포맷을 적용할 때 기본적으로 반올림 기능을 사용합니다. 그런데 컴퓨터에서 사용하는 반올림 기능와 우리가 학교에서 배워서 알고있는 반올림이 좀 다른 규칙이라는 건 알고 있나요? 아니, 반올림이 동네마다 다른 규칙을 쓴다니, 그건 어느 동네 수학이죠?
암튼, 컴퓨터에서 부동소수점 소수값을 정수로 변환하는 규칙에 관한 국제 표준이 있습니다. 우리가 학교에서 배운 반올림 방식은 나이 드신 분들이 흔히 ‘사사오입’이라 부르는 것으로 반올림하려는 자리의 숫자가 4이하이면 버리고, 5이상이면 올리는 것으로, 소수 첫째자리에서 반올림할 때, 1.5는 2가 되고, 2.5는 3이됩니다. 그런데 반올림에 관한 국제표준은 ISO 80000-1로 이 표준에서는 소수 첫째자리 숫자가 5인 경우 “가장 가까운 짝수”로 올림 혹은 내림합니다. 즉 1.5는 2가 되고 2.5 역시 2가 됩니다. 일반적으로 이것은 매우 생소하고 이상한 방식처럼 보이지만 은행 업자들에게 널리 쓰였던 방식이라고 합니다.
이와는 별개로 컴퓨터에서의 부동 소수점 계산을 어떻게 처리할지는 IEEE 754에 의해 역시 표준이 정해져 있습니다. 컴퓨터에서의 부동 소수점 숫자는 기본적으로 오차를 포함하는 어림값이기 때문에 유효 숫자를 어떤 식으로 관리하게 하여 계산 결과의 정밀성을 담보할 것인지에 대한 표준적이 방식이 필요합니다. IEEE 754는 이와 관련된 표준을 정하고 있습니다. 그리고 이 표준에는 반올림에 관한 몇 가지 방법을 제시하고 있습니다. (‘반올림’이라는 표현을 써서 우리가 일상적으로 알고 있는 반올림을 생각하면 좀 혼동이 오는데, ’round’라는 용어 자체는 끝부분이나 모서리를 다듬는다는 의미에서 반올림으로도 올림이나 내림의 의미로도 사용되는 듯 합니다.) 크게 아래의 다섯가지 방법이 있고, 계산을 적용하는 분야에 따라서 그에 맞는 규칙을 적용하여야 합니다.
- Round toward 0 – 무조건 0에 가까운 쪽으로, 양수면 내림, 음수면 올림이 됨.
- Round toward +Infiinte – 무조건 큰 쪽으로. 일반적인 ‘올림’에 해당
- Round toward -infinite – 무조건 작은 쪽으로. 일반적인 ‘내림’에 해당
- Round to nearst, ties to even – 5에 걸리면 짝수가 되는 쪽으로.
- Round to nearst, ties away from zero – 5에 걸리면 0에서 멀어지는 쪽으로.
중고등 학교 교과과정에서 음수인 소수의 반올림에 대해서 배우는지는 모르겠습니다만, 음수인 경우에 반올림까지 고려하여 이런 규칙들이 정해져 있습니다. 그리고 많은 프로그래밍 언어에서 반올림에 관한 표준으로 채택하고 있는 방식은 round to nearest, ties to even
으로 .5에 걸리게 되면 가까운 짝수쪽으로 올림이나 내림이 됩니다. 당장 파이썬이나 Julia 를 실행해서 round(2.5)
를 실행해서 그 결과로 확인해볼 수 있습니다.
물론 모든 프로그래언어가 이 규칙을 채택하고 있지는 않습니다. (제가 싫어하는)Javascript에서는 다섯번째인 “Rount to nearest, ties away from zero”를 채택하고 있습니다. 그래서 Math.round(2.5)
는 3이 나옵니다.
학교에서처럼 반올림 하는 방법
그럼 학교에서 배운 것처럼 5일 때에는 올림으로 처리하는 반올림은 어떻게 구현할 수 있을까요? 엄청 간단합니다. 소수 첫째 자리에 어떤 수가 왔을 때, 0.5를 더해 봅니다. 만약 소수 첫째 자리 숫자가 5 이상이라면 1의 자리 값이 변할 것이고, 4이하라면 그렇지 않게 되겠죠. 이 상태에서 소수점 아래를 버리고 정수로 변환하면 됩니다.
파이썬의 int()
는 float
값의 소수점 이하 자리를 모두 버리고 정수만 취합니다. 따라서,
myRound = lambda x: int(x + 0.5)
이런 식으로 간단히 구현할 수 있습니다. Julia에서는 아래와 같이 구현할 수 있겠죠.
# myRound on julia
myRound = (x) -> (x + 0.5) |> floor |> Int64
왜 이상한 규칙을 표준으로 정했을까?
그런데 하필이면 왜 이렇게 이상한 규칙을 표준으로 정한 것일까요? 심지어 학교에서는 배우지도 않는 규칙이라 당혹스럽다고 느낄 수도 있을텐데요, 여기에는 역사적인(?)이유가 있습니다. ’round-to-nearest-even’ 이라는 규칙은 다른 말로 ‘Banker’s Rounding’이라고도 합니다. 그러니까 전통적으로 금융, 회계에서는 이러한 방식을 채택해서 사용해왔습니다. 왜냐하면 ‘일상’의 반올림 규칙은 음수를 만나면 골치아픈 문제가 있거든요.
예를 들어서 수입이 2.5, 지출이 -2.5 인 상황을 생각해 봅시다. 그리고 수입과 지출이 합해지면 0으로 딱 떨어져야 하는 상황이라고 합시다. 그런데 소수점 이하는 취급하지 않기 때문에 반올림을 해야 합니다. 수입을 반올림하면 3이되고, 지출인 -2.5는 반올림하면 -2가 되어 더했을 때 0이 되지 않습니다. 이런 불일치때문에 골머리를 썩다가 고안한 방법이 바로 가까운 짝수로 맞추는 것입니다.