본문 바로가기
코딩 자산관리/Coding Test

[CNU SW 아카데미 / 코딩테스트] 파이썬 round 안되는 이유

by 은행장 노씨 2023. 1. 26.

1월 9일, 코딩 테스트를 봤다. 

기초 유형으로 이루어졌으나 내가 많이 부족하여 실수를 많이 한 코딩 테스트였다. 이번 테스트로 인해 배운 것이 많다. 

 

문제

Killometer 와 mile을 서로 변환할 수 있는 코드를 작성하세요.

  • 1 mile = 1.6 km

입력

(1) 테스트 케이스가 주어진다. 

(2) 값과 해당 값의 단위가 나온다. 

3 //테스트케이스가 3개임을 알림
1 K // 첫번째 케이스
2 M // 두번째 케이스
10 M // 세번째 케이스

출력

해당 단위에서 다른 단위의 값을 출력한다. (mile to killometer, killometer to mile)

(1) 답은 소수점 두번째 자리까지 표기(double), 마지막은 반올림함

(2) (중요)두번째 자리가 0일 경우 소수점 첫번째 자리까지만 표기

0.63 // 소수 둘째자리까지 표현.
3.2 // 소수 둘째자리 0이기에 지움.
16.0 // 소수 둘째자리 0이기에 지움.

해결방법 / 코드

문제는 단순했지만, 파이썬에는 심각한 문제가 있었다. 

바로 round()가 이상하다는 것이였다. 

분명 주석의 값을 원하고 round()를 했지만,

부동소수점 비트 계산 마냥 기준점이 짝수, 홀수인지에 따라 다르게 작동했다.

자바에서는 이러지 않았어서 처음에 엄청 당황했다. 

 

파이썬 공식문서

파이썬 공식문서를 한번 찾아봤다. 

https://docs.python.org/3/library/functions.html#round

 

Built-in Functions

The Python interpreter has a number of functions and types built into it that are always available. They are listed here in alphabetical order.,,,, Built-in Functions,,, A, abs(), aiter(), all(), a...

docs.python.org

 

출처 : 파이썬 공식문서 https://docs.python.org/3/

 

공식문서에서는 짝수 선택 방향으로 반올림된다고 적혀있었다.

즉 5의 앞자리가

  • 홀수이면 올린다. 
  • 짝수이면 내린다. 

 

수많은 해결 방법이 있겠지만,

나는 코딩 테스트용으로 빨리 해결할 수 있는 방법을 몇 가지 찾았다. 

 

(1) 아주 작은 값 더해주기

def convert(value, m):
    if m == 'K':
        return (value + 0.000001) / 1.6 		# 0.000001 을 더했다.  
    return value * 1.6

for _ in range(int(input())):
    num, m = input().split()
    print(f'{round(convert(float(num), m), 2)}')

가장 간단한 방법이다.

좋은 해결 방법은 아닌 것 같다. 

하지만 코딩 테스트 같이 빨리 결과를 낼 때는 유용하게 쓸 수 있을 것 같다. 

 

(2) decimal 모듈 사용하기

import decimal
context = decimal.getcontext()
context.rounding = decimal.ROUND_HALF_UP

def convert(value, m):
    if m == 'K':
        return decimal.Decimal(value / 1.6)			# 모듈 사용
    return value * 1.6

for _ in range(int(input())):
    num, m = input().split()
    print(f'{float(round(convert(float(num), m), 2))}')

decimal 모듈으로 구한다. 대신 마지막이 .0 처리를 해줘야 하므로 

print 하기 전에 추가로 float 처리를 했다. 

 

(3) 직접 구현하기

파이썬 함수로 직접 구현한다. 

def convert(value, m):
    if m == 'K':
        return value / 1.6
    return value * 1.6

def normal_round(num, ndgits=0):
    under_point = 10 ** (ndgits + 1)
    num = int(num * under_point)

    last_number = num % 10
    num -= last_number
    if last_number >= 5:
    	num += 10
    
    return num / under_point

for _ in range(int(input())):
    num, m = input().split()
    print(f'{normal_round(convert(float(num), m), 2)}')
  1. 소수점 아래 자리를 구하고
  2. 소수점 위로 올린다. 
  3. 마지막 값이 5를 넘는지 판단한다. 
    • 5가 넘지 않으면 마지막 값을 빼주고 (예 : 223 ---> 223 - 3 = 220)
    • 5를 넘는다면 10에서 마지막 값을 뺀 값을 더해준다(예: 228 ---> 223 + 10 - 3 = 230)
  4. 마지막으로 소수점 아래로 다시 보낸다.

 

점점 코드가 길어진다.  

함수 구현에 있어서 더 직관적이고 좋은 코드를 생각해봐야겠다. 

 

 


이번 코테로 가장 크게 깨우친 점은 바로 1번 문제였다.

내가 아는 것이 다가 아니다.  round()에서 원하는 결과값이 나오지 않아서 너무 당황했다.

이렇게 시간을 많이 쓸 줄 몰랐다.

억울하기도 하고(?), 기본을 잘 알아야겠다는 성찰도 했다.

 

 

앞으로는 이런 문제가 생겼을 때, 주저하지 않고 공식문서부터 찾아봐야겠다.