-
파이썬 round 함수의 오류 (2.5가 2가되는 마법 - 사사오입과 오사오입)Tips 2022. 9. 30. 00:33728x90반응형
코드에 결과값이 이상하게 나와서 디버깅을 하다보니 round함수를 거치니 2.5가 2가되는 현상을 발견했다.
생각해보니 예전에도 이런 적이 있었는데 또 까먹고 이걸 쓰고 있는 내 자신을 보고 이번엔 궁금증을 해결한 흐름에 따라 글로 남겨보려고 한다.
왜 2.5의 반올림이 2가 되었는가?
결론부터 말하면 파이썬의 round함수는 오사오입(5미만이면 죽이고 5이상이면 올리는)방식을 채택하고 있기 때문이다. 영어로 Banker's Rounding이라고 한다. 이 방법을 따르면 소숫점이 0.5가 아닌 경우에는 일반적으로 우리가 알고 있는 반올림과 같은 방법으로 수행되지만, 소숫점이 0.5인 경우에는 정수 부분이 짝수인 경우 소숫점을 버린다. 즉, 2.5, 4.5, 14.5 ... 는 2,4,14가 된다.
(원칙은 5를 초과할 경우 올림, 5미만일 경우 버림, 5일 경우 앞자리 수가 짝수인 경우 버림, 홀수인 경우 올림)
오사오입을 왜 사용하는가?
10진수는 5를 기준으로, 짝수와 홀수를 기준으로 반으로 나뉜다. 따라서 반올림시 오차를 최소화하기위해 짝수와 홀수에서 번갈아가며 버렸다 올렸다하는 방법을 선택하는 것이다. 따라서 숫자가 중요한 금융권, 공학, 자연과학 계열에서 많이 사용한다고 한다.
파이썬은 개발언어이고, 컴퓨터 공학이니 이 방법을 선택했나보다. 많은 프로그래밍 언어에서 반올림에 오사오입을 선택한다고 한다.
(참고 문서 : 나무위키 반올림, 엄청 자세한 블로그 포스팅: 읽어보면 프로그래밍 언어 별 채택한 반올림 정책 등 신기한 정보들이 있다)
예를 들어, 1.5와 2.5를 반올림해서 더한다 했을 때 원래 수의 합은 4다. 사사오입으로 반올림해서 더하면 5로 오차가 1이지만 오사오입으로 반올림해서 더하면 4로 오차가 0이다.
일반적인 반올림을 구현하려면 어떻게 해야하는가?
우리가 일반적으로 알고 있는 5이상이면 올리고 5미만이면 버리는 반올림은 4면 죽이고 5면 올린다고 해서 사사오입이라고 한다. 사사오입을 파이썬에서 구현하기 위해서는 세 가지 방법이 있지만 1번과 2번은 모두 비추.
1. Decimal 라이브러리 사용
일단 겁나 복잡하지만 공부했으니 적어는 둔다. 라이브러리 임포트하므로 코테에서 쓰면 바로 타임에러 각
context.rounding을 ROUND_HALF_UP으로 안 넣어주면 똑같이 오사오입 결과값으로 나오며, 숫자를 문자열로 넣었다가 다시 숫자로 변환해줘야하는 엄청난 단점이 있으므로 영원히 안 쓸듯.
import decimal context = decimal.getcontext() context.rounding = decimal.ROUND_HALF_UP num = 2.5 round_up = int(round(decimal.Decimal(str(num),0))) print(round_up) #3
2. 손수 함수 만들기
굳이 왜..
def round_up(num, digits=0): return round(val+10**(-len(str(num))-1), digits)
3. 0.5이하의 숫자를 더해줘서 소숫점을 0.5이상으로 만들기
문제를 근본적으로 접근해보면 0.5일때 올려줘야하는데 안 올려줘서 문제인 거다. 그럼 올려주게 만들면 된다.
사실 경우에 따라 그냥 정수로 만들어야하는 거면 int()함수를 쓰거나 //를 사용해서 몫만 남겨도 된다. 나의 경우 로직상 무조건 반올림을 해야하는 상황이었다.
검색하기 전에 코드를 짜서 나는 0.1을 더해줬는데 검색해보니 다들 미세한 숫자를 더하라고 적어둬서 의문이 생겼다. 0.5이하면 소숫점 첫째자리에서 반올림하는 경우에는 상관 없는 거 아닌가...?
num = 2.5 round_up = round(num/2 + 0.1)
이런 문제 말고도 이전에 파이썬에서 나눗셈이 이상하게 되는 경우도 본 것 같은데 이럴 때마다 글로 남겨둬야겠다는 생각이 든다. 아무튼 사사오입..초등학교 때 스쳐지나가며 들은 것 같은데 이젠 머릿속에 남기를
728x90반응형'Tips' 카테고리의 다른 글