<개인공부>/[Python]

[python] 파이썬 반올림 round 함수에 대해서

BlockDMask 2020. 9. 7. 00:30
반응형

안녕하세요. BlockDMask 입니다. 오늘 가지고 온 파이썬 함수는 반올림을 알 수 있는 round 함수 입니다. 파이썬의 round 함수는 제가 예상했던것과 달리 조금 특이(?)하게 동작을 하는 방식이더군요. 조금 주의해서 사용할 필요가 있다고 생각합니다.

그럼 파이썬 반올림 round 함수 포스팅을 시작해보겠습니다.

<목차>

  1. python round 함수
  1. python round 함수 예제



1. 파이썬 반올림 함수


공식 홈페이지에 가면 함수에 대한 설명이 이런식으로 나옵니다.

💡
출처 : https://docs.python.org/ko/3/library/functions.html round(number[, ndigits]) number 를 소수점 다음에 ndigits 정밀도로 반올림한 값을 돌려줍니다. ndigits 가 생략되거나 None 이면, 입력에 가장 가까운 정수를 돌려줍니다.round() 를 지원하는 내장형의 경우, 값은 10의 -ndigits 거듭제곱의 가장 가까운 배수로 반올림됩니다; 두 배수가 똑같이 가깝다면, 반올림은 짝수를 선택합니다 (예를 들어, round(0.5) 와 round(-0.5) 는 모두 0 이고, round(1.5) 는 2 입니다). 모든 정숫값은 ndigits 에 유효합니다 (양수, 0 또는 음수). ndigits 가 생략되거나 None 이면, 반환 값은 정수입니다. 그렇지 않으면 반환 값은 number 와 같은 형입니다.일반적인 파이썬 객체 number 의 경우, round 는 number.__round__ 에 위임합니다.참고 float에 대한 round() 의 동작은 예상과 다를 수 있습니다: 예를 들어, round(2.675, 2) 는 2.68 대신에 2.67 을 제공합니다. 이것은 버그가 아닙니다: 대부분의 십진 소수가 float로 정확히 표현될 수 없다는 사실로부터 오는 결과입니다. 자세한 정보는 부동 소수점 산술: 문제점 및 한계 를 보세요.

이걸 잘 펴서 정리를 해보겠습니다.

round 함수의 원형은 아래와 같습니다.

round(number [, ndigits])

반환형 : 인자로 들어온 number가 숫자 타입인 경우 정수 혹은 실수를 반환합니다. 만약 인자로 들어온 number가 숫자 타입이 아닌 경우에는 None을 반환합니다.

첫번째 인자 number 에는 반올림을 하고싶은 숫자를 집어 넣습니다.

두번째 인자 ndigits 은 정밀도에 관련된 수 입니다. 이는 자릿수를 뜻한다고 생각해도 될것 같습니다. 예를들어 소수점 2번째 까지 반올림하는것을 원하면 2를 집어 넣는식으로 진행하면 됩니다. 만약, 두번째 인자를 생략하면 첫번째 인자 number에 가장 가까운 정수를 반환합니다.

살짝 맛보기를 해보면 이렇게 됩니다.

round(0.2) # 0 반환
round(0.7) # 1 반환
round(1.6) # 2 반환


2. 파이썬 반올림 함수 예제


2-1) round 함수 예제1 : 양수

# round 함수 예제 1 : 예상값이 왜 다르게 나오지?
print('round(0.5) : {0}'.format(round(0.5)))
print('round(1.5) : {0}'.format(round(1.5)))
print('round(2.5) : {0}'.format(round(2.5)))
print('round(3.5) : {0}'.format(round(3.5)))
print('round(4.5) : {0}'.format(round(4.5)))
print('round(5.5) : {0}'.format(round(5.5)))
print('round(6.5) : {0}'.format(round(6.5)))

# round 함수 예제1의 결과
round(0.5) : 0    # 0.5의 반올림이 1이 아니라 0 ???
round(1.5) : 2
round(2.5) : 2    # 2.5의 반올림이 3이 아니라 2 ???
round(3.5) : 4    
round(4.5) : 4    # 4.5의 반올림이 5이 아니라 4 ???
round(5.5) : 6
round(6.5) : 6    # 6.5의 반올림이 7이 아니라 6 ???

나머지는 다 이해가 가는데 0.5, 2.5, 4.5, 6.5 의 결과값이 이상하네요. 0.5 반올림은 1이어야 하는데 0이 나옵니다. 엥? 만약에 0.5의 반올림이 0이면 '파이썬 반올림은 X.5일때 내림인가?' 싶어서 1.5도 그럼 1이겠네 싶지만, 1.5를 반환하면 2가 나오게 됩니다.

파이썬의 반올림반올림 하려는 수올림, 내림했을 때 동일하게 차이가 나는 경우에는 짝수 값으로 반올림 합니다. 위에서 결과 값으로 확인했듯이

0.5 는 0에도 0.5가 차이나고 1에도 0.5가 차이가 나는 상황입니다. 이때 파이썬의 round 함수는 똑같이 가깝다면 짝수를 반환하게 설계되어있기 때문에 0 을 반환하는 것 입니다.

다시한번더 말씀드리면

2.5 라는 숫자는 2에도 3에도 똑같이 0.5만큼 차이가 나기 때문에 짝수값인 2라는 수를 반올림의 적절한 값으로 판단하여 반환합니다.

정말 마지막으로 다시 한번 더 말씀 드리면

3.5 라는 숫자는 3에도 4에도 똑같이 0.5 만큼 차이 나기 때문에 짝수 값인 4라는 수를 반올림의 적절한 값으로 판단하여 반환합니다.

2-2) round 함수 예제2 : 음수

# round 함수 예제 2
print('round(-6.5) : {0}'.format(round(-6.5)))
print('round(-5.5) : {0}'.format(round(-5.5)))
print('round(-4.5) : {0}'.format(round(-4.5)))
print('round(-3.5) : {0}'.format(round(-3.5)))
print('round(-2.5) : {0}'.format(round(-2.5)))
print('round(-1.5) : {0}'.format(round(-1.5)))
print('round(-0.5) : {0}'.format(round(-0.5)))

# round 함수 예제2의 결과
round(-6.5) : -6
round(-5.5) : -6
round(-4.5) : -4
round(-3.5) : -4
round(-2.5) : -2
round(-1.5) : -2
round(-0.5) : 0

음수도 마찬가지 입니다.

-2.5 라는 수를 반올림 하려 할 때, -2와 -3이 동일하게 0.5 차이로 가깝기 때문에 짝수 값인 -2를 반환하게 됩니다.

-1.5 라는 수를 반올림 하려 할때, -1과 -2가 동일하게 0.5 차이로 가깝기 때문에 짝수 값인 -2를 반올림의 적절한 결과값이라 판단하고 반환합니다.

2-3) round 함수 예제3 : 부동소수점이 함정을 만듭니다.

반올림 예제 1번과 2번에서는 '1.5', 2.5'와 같이 우리가 직접 소수점을 정확하게 입력을 해서 반올림을 구해보았습니다.

만약에 소수점을 한번 더해서 반올림을 구해도 똑같이 나올까요? 0.0 부터 0.1씩 더하면서 반올림을 한번 구해보았습니다.

# 0.0 부터 0.1씩 더하면서 3.0 까지 round 함수로 반올림을 해보았습니다.
a = 0.0
while a < 2.1:
    result = round(a)
    print('round({0}) => {1}'.format(a, result))
    a += 0.1

# 프로그램 실행 결과
round(0.0) => 0
round(0.1) => 0
round(0.2) => 0
round(0.30000000000000004) => 0
round(0.4) => 0
round(0.5) => 0 # 주요 요점1
round(0.6) => 1
round(0.7) => 1
round(0.7999999999999999) => 1
round(0.8999999999999999) => 1
round(0.9999999999999999) => 1
round(1.0999999999999999) => 1
round(1.2) => 1
round(1.3) => 1
round(1.4000000000000001) => 1
round(1.5000000000000002) => 2 # 주요 요점2
round(1.6000000000000003) => 2
round(1.7000000000000004) => 2
round(1.8000000000000005) => 2
round(1.9000000000000006) => 2
round(2.0000000000000004) => 2
round(2.1000000000000005) => 2
round(2.2000000000000006) => 2
round(2.3000000000000007) => 2
round(2.400000000000001) => 2
round(2.500000000000001) => 3 # 주요 요점3
round(2.600000000000001) => 3
round(2.700000000000001) => 3
round(2.800000000000001) => 3
round(2.9000000000000012) => 3
round(3.0000000000000013) => 3

부동소수점은 소수점이 정확하지 않다는 특징이 있습니다. 그게 무슨 이야기 인가 하면 우리가 사용하는 10진 소수의 수를 float가 정확하게 표현하지 못하기 때문인데요. 이것과 관련해서는 따로 구글링으로 찾아보시기 바랍니다. 아무튼 우리는 이 부정확함이 반올림에서 어떤 효과를 불러오는지 한번 알아보겠습니다.

위 프로그램은 0.0 부터 0.1을 더해가면서 3.1이 넘지 않는 소수를 반올림하는 프로그램 입니다. 위 프로그램을 보면 하나의 특징이 있습니다. 분명 우리는 정확하게 0.1을 더해주고 있는데 어디선가 부터 오차가 발생하기 시작합니다. 0.3000000~0004 와 같이 갑자기 소수점 쩌아래에 4가 더해지는가 하면, 0.7999999~9999와 같이 오차가 생기기 시작합니다. 심지어 0.8은 0.79999999~ 로 표현되기도 합니다.

이게 바로 부동소수점의 오차, 우리가 쉽게 함정에 빠질 위험이 있는 경우 입니다. 제가 표기한 주요 요점 1,2,3 번을 한번 보시기 바랍니다. 0.5 는 우연히도 오차가 따로 없이 0.5 값이 들어갔기 때문에 파이썬 형식의 짝수 반올림에 0.5가 들어가서 0이 적절하게 반환 되었습니다.

하지만 주요 요점 2를 보면 1.5가 들어가 있어야할 자리에 1.50000~2가 들어가 있습니다. 우리가 예상한 round(1.5)의 반환 값은 1과 2 사이에 0.5씩 동일하게 차이가 나서 2가 나와야하는데, 2에 가까워서 2가 반환이 된것 입니다. 결론적으로 round(1.5000~2)도 round(1.5)와 반환형이 같으니까 결과값이 정상? 같아 보이긴 합니다.


그럼 주요 요점 3번을 한번 볼까요? round(2.5)의 반환값은 2와 3사이의 차이가 0.5로 동일해서 짝수값을 반환해야 하기 때문에 2를 반환하는게 적절한 반환입니다.

하지만! 여기서 부동소수점 오차에 의해서 round(2.5)가 아닌 round(2.50000~1)이 들어가 있는걸 보셨죠? 부동소수점이 저래요. 우리 예상을 뛰어넘습니다. 아주 못된놈 입니다. 무튼 결론적으로 부동소수점을 이용해서 연산을 할때는 우리 예상과 다른(=근접한) 값이 들어가기 때문에 조심해야합니다. round 함수에서도 조심! 또조심! 입니다.


지금까지 파이썬 반올림 함수에 대해서 알아보았습니다. 감사합니다.

반응형