본문 바로가기
Python/파이썬 문법 Tips

[ 파이썬(python) ] 절사평균과 round(반올림) 함수 그리고 부동소수점 비교

by YWTechIT 2021. 7. 13.
728x90

📍 절사 평균과 round(반올림) 함수 그리고 부동소수점 비교

절사평균 - 위키피디아 란, 편차가 큰 자료의 경우 산술평균이 적합하지 않으므로 자료의 총 개수에서 일정 비율만큼 가장 큰 부분과 가장 작은부분을 제거 후 평균을 산출하는 방법이다.

 

예를들어, `n=5`개인 표본집단에서 15%의 절사평균을 구한다고 하면 전체 값의 상위 5 * 0.15 = 0.75, 하위 5 * 0.15 = 0.75개 만큼 제외 후 평균을 구한다는 뜻이다. (문제에 따라 소수점으로 나오게 되면 반올림을 하거나 하지 않는 경우가 있기 때문에 반올림하지 않았다.)

 

주어진 표본집단 arr에서 15% 절사평균을 구한다고 가정하면 (소수점일 때 반올림한다.) python에서 다음과 같이 작성 할 수 있다. 이때 slicing을 2가지 방법으로 구현했다.

 

n = 5
arr = [10, 20, 30, 40, 50]

trunc = round(n * 0.15)    # 1, 앞 뒤로 1개씩 제외시키기

print(arr[trunc: -trunc])    # arr[1:-1]
👉🏽 [20, 30, 40] 

print(arr[trunc: n-trunc])    # arr[1:4]
👉🏽 [20, 30, 40]

 

논외로, 알고리즘 문제(예: boj_18110 - solved.ac)에서 `round` 함수를 사용해서 제출하면 오답 판정을 받는데 왜냐하면 python에서 round 함수를 사용할 때 우리가 알고 있는 반올림 형태인 4사 5입(4 이하는 버리고 5 이상부터 반올림)이 아니라 5사 5입(ROUND_HALF_EVEN or round_to_nearest_even: 앞자리가 홀수면 올리고 앞자리가 짝수면 버리는 방법)을 기반으로 실행되기 때문이다.

728x90

공학이나 자연과학에서 5사 5입 방법을 많이 사용하는데 맨 뒷자리가 필연적으로 손실되는 계산 특성상 오사오입으로 처리하는 것이 오차가 가장 적기 때문이다. 추가적인 내용이 궁금하다면 부동 소수점의 문제점과 한계(python.org)를 살펴보자.

 

5사 5입(ROUND_HALF_EVEN or round_to_nearest_even)의 예시는 다음과 같다.

print(round(0.5))    # 0
print(round(1.5))    # 2
print(round(2.5))    # 2
print(round(3.5))    # 4
print(round(4.5))    # 4
print(round(5.5))    # 6

 

결론적으로 부동소수점을 반올림을 할 때 우리가 평소에 사용하는 십진법으로 계산하고 싶다면 직접 함수를 만들거나, decimal , `Fraction` 라이브러리를 이용해야 하는데 다음과 같이 사용 할 수 있다.

# 함수 만들기
def round2(num):
    return int(num) + (1 if num - int(num) >= 0.5 else 0)

print(round2(0.5))    # 1
print(round2(1.5))    # 2
print(round2(2.5))    # 3
print(round2(3.5))    # 4
print(round2(4.5))    # 5
print(round2(5.5))    # 6

# decimal 라이브러리 사용
from decimal import Decimal
value = Decimal('0.1') * Decimal('0.1')

print(value)
👉🏽 0.01

 

여담이지만, 두 실수가 같은지 판단하려면 `==` 대신 `math.isclose` 혹은 `sys`를 이용해서 확인 할 수 있다. 참고로 `sys.float_info.epsilon`에 저장된 값은 머신 앱실론(machine epsilon)이라고 부르는데, 어떤 실수를 가장 가까운 부동소수점 실수로 반올림 했을 때 상대 오차는 항상 머신 앱실론 이하다. 그래서 두 값이 같은지 다른지를 비교하려면 비교대상을 머신 앱실론으로 정하고 이보다 작거나 같으면 `True` 처리가 된다.

 

또, `python 3.5` 이상부터는 `math_isclose` 함수를 사용하여 비교 할 수도 있다.

# epsilon 사용
import math, sys

x = 0.1 + 0.2
print(math.fabs(x - 0.3) <= sys.float_info.epsilon)
👉🏽 True

print(sys.float_info.epsilon)
👉🏽 2.220446049250313e-16    # epsilon 값

# math.isclose 사용
print(math.isclose(0.1 + 0.2, 0.3))
👉🏽 True

 

reference

  1. naver-blog
  2. python - decimal 라이브러리
  3. 코딩도장
반응형

댓글