kotlin.math.round()... 뭔가 이상한데?
https://uknowblog.tistory.com/337
백준 18110번. solved.ac를 풀면서 한 가지 문제를 만났습니다.
로직 구현 자체는 꽤 간단하고, 딱히 오류도 없는 것 같은데 자꾸 틀렸습니다를 받았기에,
왜지..? 곰곰히 생각하다가,
파이썬에서도 반올림 방식(오사오입)으로 비슷한 경험을 했기에,
혹시 코틀린의 round()가 오사오입인가??? 하는 마음에 한 번 테스트를 해봤습니다.
네. 오사오입이 맞았습니다.
일상생활에서 반올림을 한다면 주로 어떤 방식으로 할까요?
아마 대부분은 1, 2, 3, 4면 내리고, 5, 6, 7, 8, 9면 올리지 않을까 싶네요.
이러한 방식을 사사오입이라 합니다.
5를 기준으로 5보다 작으면 내림, 5와 같거나 크면 올림.
하지만 파이썬과 코틀린의 반올림은 조금 다릅니다.
1, 2, 3, 4는 내림. 6, 7, 8, 9면 올림.
5의 앞자리가 홀수일 경우엔 내림, 5의 앞자리가 짝수일 경우엔 올림.
1.5의 경우 2, 2.5의 경우 2, 3.5의 경우 4, 4.5의 경우 4, 이와 같이 짝수를 향하도록 반올림을 하는 것이지요.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/round.html
코틀린 공식 문서를 참고해봤는데,
가장 가까운 정수로 반올림하되, 동률일 때(5)는 짝수(Even Integer)를 향해 반올림을 한다네요.
즉, 1.0 ~ 1.4는 1로, 1.6~1.9는 2로,
1.5의 경우 1.0, 2.0 모두 거리가 같으니, 2로 반올림을 합니다.
이는 자바의 Math.rint() 메소드와 동일한데,
kotlin.math.round를 타고 들어가보면,
내부적으로 java.lang.math의 rint() 메소드를 호출하고 있는 것을 볼 수 있습니다.
참고로 nativeMath는 java.lang.Math를 as를 사용하여 이름을 붙여 import 해준 것 입니다.
그럼 사사오입 방식으로 반올림을 하려면?
그럼, 우리가 흔히 알고 있는 사사오입(5면 그냥 올림) 방식으로 반올림을 하려면 어떻게 해야 할까요?
kotlin.math.roundToInt() / kotlin.math.roundToLong() 사용
의도한 대로 앞자리가 홀수건 짝수건간에,
5면 그냥 올림처리를 해줍니다.
내부적으로 java.lang.Math의 round를 사용하고 있네요.
java.lang.Math의 round() 사용
코틀린의 가장 큰 장점중 하나는 자바와 상호운용을 지원한다는 점입니다.
따라서 그냥 자바의 java.lang.Math의 round를 불러와 사용해도 됩니다.
근데 왜 오사오입을 쓰는거지?
자바의 round는 기본적으로 사사오입 이지만,
코틀린과 파이썬의 경우는 오사오입을 사용합니다.
그런데, 왜 오사오입을 쓰는 걸까요?
어째서 앞자리가 홀수냐 짝수냐를 신경쓰는 걸까요?
그것은 0.5가 경계값이기 때문입니다.
소숫점 한자리 까지만 계산한다는 가정 아래, 7.0 ~ 7.9를 반올림 해봅시다.
반올림 대상이 되는 수는 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9 입니다.
5 이상이면 그냥 올릴 경우,
내림 - 7.1, 7.2, 7.3, 7.4 4개
올림 - 7.5, 7.6, 7.7, 7.8, 7.9 5개로,
5가 경계값에 딱 걸치기 때문에, 5를 그냥 올림처리 해버리면 편향적인 결과가 도출될 수 있습니다.
하지만 앞자리에 따라 다르게 반올림을 한다면?
사실 통계적으로 봤을 때는 상당히 합리적인 방법입니다.
이를 은행가 반올림 (Banker's Rounding)이라고 하는데,
5를 그냥 올림 처리할 경우, 사실 별 문제는 없어 보이지만,
데이터가 많다면?
실제 돈이 움직인다면?
그것도 몇 천, 몇 억씩 움직이는 금융권이라면? 금전적 손실은 꽤 커질 수 있습니다.
마침
반올림 문제로 계속 '틀렸습니다'를 받은 것에서 시작해,
코틀린의 반올림 방식과 오사오입을 찾다가,
은행가 반올림까지 알게 되었네요.
코딩을 하다보면 참 신기한걸 많이 알아갑니다.
'언어 > Kotlin' 카테고리의 다른 글
Kotlin에서의 Setter를 다루는 방법 (1) | 2024.01.30 |
---|---|
코틀린에서는 왜 더 큰 타입으로 자동변환되지 않을까? (0) | 2023.10.16 |
[Kotlin/코틀린] 여러 조건을 기준으로 정렬하기 (0) | 2023.05.21 |
자바의 StringBuilder 개행문자 삽입방법 - append(str + "\n") vs append(str).append("\n") (0) | 2023.02.01 |
[Kotlin] BigDecimal을 사용하여 매우 큰 수, 소숫점 처리하기 (0) | 2022.12.21 |