[Python] round()함수와 round-half-even

송희
6 min readOct 13, 2022

--

흥미로운 주제로 혼자 알기가 아까운 마음이 들었습니다. 더불어 round-half-even을 사용하는 이유에 대해 다룬 글을 찾기가 어려워, 이러한 점에 관심 있는 분들께 도움이 되고자 글을 남깁니다.

Photo by Chaitanya Tvs on Unsplash

round-half-even 방식이란?

round-half-even을 사용하는 이유

round-half-even에 대한 대책

다음 코드의 결과는 무엇이라고 생각하십니까?

print(round(9/2))
print(round(7/2))
print(round(3/2))

아마 대부분의 사람들은 5, 4, 2를 말하지 않을까 싶습니다. 놀랍게도 이 구문의 실행결과는 4,4,2입니다.

왜냐하면 파이썬의 round는 round-half-even의 방식을 따르기 때문이죠. 정확히는 round-half-even을 따른다기보다는, round-half-even이 되는 상황에서는 round-half-even을 이용한다는 것이 맞는 말인 것 같습니다.

round-half-even 방식이란?

round-half-even 방식이란, 본 값에서 올림과 내림이 정확히 같은 차이를 보일 때 짝수에 가까운 쪽으로 올림을 하는 것입니다.

앞서 말씀드린 예시를 보고 다시 설명하자면, 다음과 같습니다.

9/2 = 4.5
  • 올림(round-up) :5
  • 내림(round-down):4

4.5에서 올림까지 +0.5, 내림까지 -0.5

짝수에 가까운 4로 반올림

7/2 = 3.5
  • 올림(round-up) :4
  • 내림(round-down):3

3.5에서 올림까지 +0.5, 내림까지 -0.5

짝수에 가까운 4로 반올림

3/2 = 1.5
  • 올림(round-up) :2
  • 내림(round-down):1

1.5에서 올림까지 +0.5, 내림까지 -0.5

짝수에 가까운 2로 반올림

round-half-even를 사용하는 이유

우리가 일상적으로 사용하는 반올림은 반올림의 한 방법일 뿐입니다. 반올림은 생각보다 여러 가지 방법을 가지고 있습니다.

  • 정수로 직접 반올림( 내림, 반올림, 0으로 반올림, 0에서 반올림)
  • 가장 가까운 정수로 반올림(반올림, 반내림, 0쪽으로 반올림 등)
  • 정수로 무작위 반올림(무작위, 교대로, 확률적)
출처 : geeksforgeeks.org

다만 위의 그림을 참고할 때 올림은 항상 +∞(양의 무한대)로, 내림은 항상 -∞(음의 무한대로) 근사되기 때문에 이에 대한 오차가 생길 수 밖에 없습니다.

예컨대, 1/3과 같은 값들은 올림을 택하든, 내림을 택하든 항상 오차가 발생해 정확한 값을 나타낼 수 없습니다.

그래서 수학적으로 이에 대한 오차의 편향을 최대한 줄이고자 고안된 것이 round-half-even 입니다.

round-half-even에 대한 대책

round-half-even이 사용됐는지를 이해하면 대책이라 말하기가 뭐하지만, 어쨌든 원하는 값으로 올림을 하고 싶다면 이 문제를 잘 다스릴 필요가 있습니다.

이에 대한 방법은 크게 다음의 두 가지 방법입니다.

  • 함수를 직접 정의해 0.5를 더 해 int로 출력해주는 함수를 만들거나
def HalfRoundUp(value):
return int(value + 0.5)
  • decimal를 이용하는 방법
from decimal import Decimal, ROUND_HALF_UP

Decimal(1.5).quantize(0, ROUND_HALF_UP)

# This also works for rounding to the integer part:
Decimal(1.5).to_integral_value(rounding=ROUND_HALF_UP)

개인적으로 전자의 방법은 직접적으로 값을 변형하기 때문에 추천드리는 방법은 아닙니다. 되도록이면 후자의 방법을 사용하는 것을 추천드립니다.

공식문서를 참조하면 decimal의 여러 반올림 모듈을 이용할 수 있습니다.

기본으로 설정되어 있는 모듈은 ROUND_HALF_EVEN입니다.

print(decimal.getcontext())
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])

다음과 같이 모듈을 바꿔서 이용할 수 있습니다.

print(decimal.getcontext())
print(Decimal('4.5').quantize(Decimal('1.')))

decimal.getcontext().rounding = ROUND_HALF_UP
print(decimal.getcontext())
print(Decimal('4.5').quantize(Decimal('1.')))
>> Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])
>> 4
>> Context(prec=28, rounding=ROUND_HALF_UP, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[Inexact, Rounded], traps=[InvalidOperation, DivisionByZero, Overflow])
>> 5

--

--

송희
송희

Written by 송희

커피와 책, 구름을 좋아하는 개발자입니다. 고민의 과정을 담고자 노력하고 있습니다. Github : https://github.com/song-hee-1