본문 바로가기
프로그래밍 공부(정리)/Python

[Python] 소수점 n번째 자리에서 반올림

by 프룹 2023. 2. 6.
반응형

Python에서 round함수는 오사오입 법칙을 따른다.

사사오입

사사오입(四捨五入)

십진법에서는 다음과 같이 반올림을 한다.

  1. 반올림 할 자리를 구한다.

4 이하이면 0으로 버리고 5 이상이면 0으로 버린 후 윗자리에 1을 더한다.

사사오입의 예

  • 73
    • 일의 자리에서 반올림: 70
    • 십의 자리에서 반올림: 100
  • 51.6137
    • 소수점 넷째 자리에서 반올림: 51.614
    • 소수점 셋째 자리에서 반올림: 51.61
    • 소수점 둘째 자리에서 반올림: 51.6
    • 소수점 첫째 자리에서 반올림: 52
    • 일의 자리에서 반올림: 50
    • 십의 자리에서 반올림: 100

오사오입

반올림에서 5 미만의 숫자는 버림 하며초과의 숫자는 올림한다.올림 한다. 5의 경우에는 5의 앞자리가 홀수인 경우엔 올림을 하고 짝수인 경우엔 버림을 하여 짝수로 만들어준다. 예를 들어 53.45는 53.4로, 32.75는 32.8로 반올림한다. 이를 오사오입(round-to-nearest-even)이라고 한다. 자연과학 및 공학의 유효 숫자에서 많이 쓴다.

출처 : https://ko.wikipedia.org/wiki/반올림

 

 

그래서 사사오입의 방식으로 반올림을 하려면 별도로 구현해야 한다.

 

구현코드


## 1번 방법
def round_half_up(num, dec=0):
    from decimal import Decimal

    # 3.892857142857143 5.869642857142857 9.7625 9.762 9.763

    num = list(str(num))
    if "." not in num:
        # 자연수
        return int("".join(num))
    idx = num.index(".")
    if len(num) - 1 <= idx + dec:
        # dec보다 낮은 소수점 자리수
        return float("".join(num))
    if int(num[idx + dec + 1]) >= 5:
        # 반올림이 필요한 경우
        num = "".join(num[: idx + dec + 1])
        left = f"0.{'0'*(dec-1)}1"
        return float(Decimal(num) + Decimal(left))
    else:
        num = float("".join(num[: idx + dec + 1]))
        return num

## 2번 방법
import math
def round_half_up(n, dec=0):
    multiplier = 10**dec
    return math.floor(n * multiplier + 0.5) / multiplier

1, 2번 어떤 방법이든 상관없다!

 

착각하기 쉬운 방법

f"{123.4556:.3f}"
# 출력 : 123.456

구글링을 하다 보면 f-string을 이용하면 round를 구현할 수 있다고 쓰여있는데, 막상 해보면 round와 똑같은 오류를 범한다.

검증용 코드

import random

def round_half_up(num, dec=0):
    from decimal import Decimal

    # 3.892857142857143 5.869642857142857 9.7625 9.762 9.763

    num = list(str(num))
    if "." not in num:
        # 자연수
        return int("".join(num))
    idx = num.index(".")
    if len(num) - 1 <= idx + dec:
        # dec보다 낮은 소수점 자리수
        return float("".join(num))
    if int(num[idx + dec + 1]) >= 5:
        # 반올림이 필요한 경우
        num = "".join(num[: idx + dec + 1])
        left = f"0.{'0'*(dec-1)}1"
        return float(Decimal(num) + Decimal(left))
    else:
        num = float("".join(num[: idx + dec + 1]))
        return num

for i in range(100000):
    a = random.randint(10000, 100000) / random.randint(1000, 10000)
    b = random.randint(10000, 100000) / random.randint(1000, 10000)
    c = a + b

    if float(f"{c:.3f}") != round(c, 3) or float(f"{c:.3f}") != round_half_up(c, 3):
        print(a, b, c, f"{c:.3f}", round(c, 3), round_half_up(c, 3))
반응형

댓글