Banker’s Rounding – 은행원 방식 반올림

반올림은 여러가지 방식이 있습니다. 이 때문에 소숫점이 있는 수치 계산을 하다가 오차가 발생해서 왜 그럴까 고민하는 일이 생기고 반올림 방식의 차이가 있다는 것을 모르면 헤맬 수 있습니다.

제가 학생이던 시절에 금전 계산을 자동으로 복잡하게 하는 소프트웨어를 의뢰받아 만든 적이 있었는데 의뢰인이 준 계산 방법과 확인용 정산 출력물을 기반으로 계산 소프트웨어를 작성했었습니다. 그런데 받은 계산법으로 개별 계산이 일치하는 것을 확인하고 점점 단계를 여러 번 거치고 마지막 총합을 확인해보니 정답지와 결과값이 미세하게 달라서 문제의 원인을 찾느라 시간을 허비한 적이 있습니다.

원인은 제가 사용한 컴퓨터 랭귀지의 round 함수가 사사오입이 아닌 뱅커스 라운딩을 했기 때문에 발생한 문제였고 round 함수를 사사오입 방식의 다른 round로 변경했었습니다. 그 뒤로는 round를 하게될 일이 있으면 반드시 사용하는 소프트웨어의 round가 사사오입 방식인지 뱅커스 라운딩인지 확인을 하고 작업을 시작하게 되었습니다. 또는 오차가 좀 크더라도 정합성을 위해서 무조건 소숫점이하는 절사 시켜버리곤 했습니다. (물론 요즘은 이것도 잘 안합니다)

어쨌든 당시 컴퓨터 랭귀지의 기본함수에 버그가 있는 줄 알고 리포트를 하려고 했었으나 천절하게 설명을 해 두었더군요. 그리고 관련 자료를 더 찾아보고는 제가 무식했다는 사실을 알았습니다.

“세상에 반올림 방법은 딱 하나 인 줄 만 알았어”

앞서 말씀드린 것 처럼 반올림은 여러가지 방식이 있습니다. 꽤 많은 방식들이 있습니다만 우리가 흔히 접할 수 있는 것은 위에서 말한 2가지입니다.
이 포스트에서 그 차이를 간단하게 설명하겠습니다.

사사오입( 四捨五入 ) 반올림

우선, 흔히 아는 반올림은 사사오입( 四捨五入)인데 4는 버림, 5는 올림을 뜻합니다. 보통은 우리는 그냥 “반올림”이라고 하지만 명확환 구분과 설명을 위해서 “사사오입”이라고 하겠습니다.
사사오입(Normal Rounding)은 영어로는 Round, away from zero (0에서 멀어지는) 라고 합니다.
소숫점이 있는 숫자들을 정수로 반올림한다고 하면 다음과 같이 되는 것입니다.

0.4 반올림 하면 0
0.5 반올림 하면 1
0.6 반올림 하면 1
1.4 반올림 하면 1
1.5 반올림 하면 2
1.6 반올림 하면 2
2.4 반올림 하면 2
2.5 반올림 하면 3
2.6 반올림 하면 3

뱅커스 라운딩

뱅커스 라운딩( Banker’s rounding , 금융업에서 은행원들이 사용하는 반올림)은 올려질 값이 5인 경우에 짝수가 되도록(짝수에 수렴) 하는 차이가 있습니다.
영어의 다른 말로는 Round, half to even (절반을 짝수쪽으로)라고 합니다. 금융권에서 채택해서 쓰기 때문에 뱅커스 라운딩이라는 별칭으로 많이 불립니다. 미국의 경우이고 한국 금융권에서도 이것을 채택해서 쓰는지는 모르겠습니다.
어쨌든 다음과 같이 계산 됩니다. 굵은 글씨 부분을 자세히 보세요.

0.4 반올림 하면 0
0.5 반올림 하면 0
0.6 반올림 하면 1
1.4 반올림 하면 1
1.5 반올림 하면 2
1.6 반올림 하면 2
2.4 반올림 하면 2
2.5 반올림 하면 2
2.6 반올림 하면 3
2.45 반올림 하면 2

둘을 비교 해보면 이렇습니다.

대상값 / 방식사사오입뱅커스 라운딩
0.400
0.510
1.411
1.522
1.622
2.422
2.532
2.633
4.554
4.444554

뱅커스 라운딩은 미국 표준이라고 알려져 있습니다(직접 확인은 안해봤습니다). 최신의 컴퓨터 언어에서 기본으로 지원하는 반올림 함수는 우리가 아는 사사오입 방식이 아닌 뱅커스 라운딩을 사용하는 것이 많습니다. 특히 과학계산 용도로 쓰는 것들이 그렇습니다.

반올림?

사사오입은 엄밀히 말하면 반올림이 아닙니다. 영어로 round라고 하는 것은 숫자를 단순하게 만드는 것을 말합니다.

“무조건 올림”인 round-up 과 “무조건 내림”인 round-down도 round 계열이지만 이것들까지 반올림이라고 부르는 것은 매우 이상하다고 느낄 것입니다.

그리고 사사오입의 영문 표현이 away from zero인 것을 보시면 5를 0에서 멀어지게 하는 것을 뜻하는데 반올림이라고 표현하는 것에 무리가 있다고 합니다. 반면 half to even 방식은 짝수쪽으로 반을 주기 때문에 이것이 진짜 반올림입니다.

하지만 이미 고착되었기 때문에 편의상 다 “반올림”이라고 부르겠습니다.

뱅커스 라운딩을 사용하는 이유

당연히 반올림에서 발생하는 오차를 줄이기 위해서 고안된 것입니다.

숫자 1과 2가 있고 이 사이에 소숫점 첫째자리의 수만 보면 다음과 같이 9개가 숫자가 있습니다. 10개가 아닙니다.

0.10.20.30.40.50.60.70.80.9

0.5가 한가운데 있기 때문에 절사해서 버릴 것인지 올려줄 것인지가 고민이 됩니다. 어느쪽으로 하든 오차를 만듭니다. 사사오입 방식은 5를 항상 위로 올리기 때문(away from zero)에 여기서 숫자가 원래 올리지 않았을 때 보다는 커지는 일이 많아집니다. 그리고 공평하지 않습니다.

대신 뱅커스 라운딩 처럼 짝수쪽으로 수렴하게 해서 조금은 공평해집니다. 이 방식이 오차가 덜 발생한다는 것은 오래전에 증명되었다고 합니다.

그외의 반올림 방식

위키피디아의 rounding 페이지를 보시면 rounding방식이 상당히 많다는 것을 보고 놀랄 수 있습니다. 반올림 문제는 메소포타미아에도 기록이 남아 있는 오래된 문제라고 되어 있습니다. 그리고 뱅커스 라운딩이라고 불리는 half to even 방식은 1940년도 나온 것이라고 적혀 있습니다. 오래되었죠.

위키피디아: https://en.wikipedia.org/wiki/Rounding

비교표

너무 많은 round를 알 필요는 없겠지만 대략 아래와 같은 것이 있습니다.

round 함수의 방식 차이로 인한 정합성 문제

모르고 계셨을지 모르겠지만 뱅커스 라운딩을 기본으로 채택해서 제공하는 컴퓨터 언어가 좀 많습니다.

R, Python3과 같은 것들이 그렇습니다. 그 외의 랭귀지도 꽤 많습니다. Excel과 RDBMS 같은 소프트웨어의 반올림 함수는 대부분 우리가 아는 “사사오입”으로 되어 있습니다. 그러다보니 행수가 많은 데이터에서 나누기, 곱하기 같은 것들이 여러번 반복하고 그 결과를 모두 합한다거나 평균을 구한다거나 하면 동일한 데이터를 가지고 계산을 해도 사용하는 컴퓨터 언어나 툴에 따라 양쪽의 결과값에 미묘한 차이가 발생합니다.

즉, round함수의 방식이 다른 것을 각각 따로 어떤 데이터의 사칙 연산을 하면서 반올림이 섞인 계산을 반복하는 경우에는 양쪽에서 똑같이 계산해도 round의 방식으로 인해 서로 결과값이 안맞는 문제가 발생합니다.
이때 정합성 오류가 부동소숫점 연산 문제나 반올림 방식의 차이로 인한 문제라는 것을 알면 그것은 해결하기도 어렵고 원인을 알면 오차를 감수하고 넘어갈 수도 있지만, 그것을 모르면 오차가 어디서 발생했는지 원인를 찾기 위해서 시간을 허비하게 됩니다.

버그인지 계산을 잘못한 것인지…

그래서 차이가 있다는 것을 알고 있는 것이 좋습니다.
물론 뱅커스 라운딩을 기본으로 채택한 것들은 별도로 사사오입을 지원하는 함수를 따로 제공하는 것이 많습니다. (아닌 것도 있는데 그럴 때는 구현해야 합니다)

컴퓨터 언어, 소프트웨어의 기본 반올림 방식

직접 몇 개를 확인해 봤습니다. 결과는 아래 테이블에 있습니다.

언어/소프트웨어방식
Linux shell (printf)뱅커스 라운딩
R뱅커스 라운딩
Python2사사오입
Python3뱅커스 라운딩
C/C++사사오입
Object-C사사오입
C#뱅커스 라운딩
Visual Basic뱅커스 라운딩
Go사사오입
Ruby사사오입
Scala사사오입
PHP사사오입
Julia뱅커스 라운딩
Javascript사사오입
Java사사오입
Erlang사사오입
Pascal뱅커스 라운딩
Cobol뱅커스 라운딩 (기본)
Fortran사사오입
Excel사사오입
Google Spread Sheet사사오입
MySQL사사오입
Hive사사오입
Redshift (PostgreSQL?)사사오입
BigQuery사사오입
Oracle사사오입
SQLite사사오입
Matlab사사오입
Mathematica뱅커스 라운딩
WolframAlpha뱅커스 라운딩
Tableau사사오입

보시면 알겠지만

  • Linux에 있는 printf뱅커스 라운딩 (코맨드마다 다를 수도 있습니다)
  • Java 계열사사오입
  • 닷넷(.net) 계열뱅커스 라운딩 (.net이 아닌 MFC를 확인 안해봤습니다. 귀찮아요)
  • 데이터에 중점을 둔 것들은 사사오입
  • 과학, 공학에 중점을 둔 것은 뱅커스 라운딩
  • 함수형 언어뱅커스 라운딩
  • 시스템 개발에 중점을 둔 언어는 사사오입
  • Python은 2에서 3으로 바뀔 때 뱅커스 라운딩을 기본으로 바꿈

그리고 매우 오래전부터 사용해온 것들은 일관성과 정합성으로 인해 별도로 뱅커스 라운딩 함수를 제공하고 기본 함수의 방식을 바꾸지는 않는 것 같습니다. 뱅커스 라운딩이 더 권고되는 표준이기 때문에 새로 만들어지는 것들은 뱅커스 라운딩을 지원해야 한다고 하는 사람이 많은 분위기로 보입니다.

특히 데이터베이스는 잘 쓰고 있는데 어느날 업그레이드를 하고 나서 계산값이 달라지거나 합계를 했는데 총합이 달라지거나 하게 되면 문제가 커질 수 있으니까요.

반올림의 인해 엄청난 문제가 발생하는 경우는 일상 생활에 별로 없겠습니다. 하지만 수능점수같은 것은 반올림 문제로 학생의 인생이 바뀔 수도 있어서 매우 골치가 아플것 같습니다.

어쨌든 정밀한 수치 확인이 필요한 작업을 혹시 하게 되는 경우를 위해서 알아두시면 좋을 것 같습니다.