“round” 및 “Decimal.quantize를 사용하여 Python에서 소수 및 정수 반올림

사업

다음은 파이썬에서 짝수로 반올림하거나 반올림하여 숫자를 반올림하는 방법을 설명합니다. 숫자는 부동 소수점 부동 소수점 또는 정수 int 유형으로 간주됩니다.

  • 내장 함수(예: 프로그래밍 언어):round()
    • 소수를 임의의 자릿수로 반올림합니다.
    • 정수를 임의의 자릿수로 반올림합니다.
    • round()는 일반적인 반올림이 아닌 짝수로 반올림합니다.
  • 표준 라이브러리decimalquantize()
    • Decimal개체 만들기
    • 소수를 임의의 자릿수로 반올림하고 짝수로 반올림
    • 정수를 임의의 자릿수로 반올림하고 짝수로 반올림
  • 새 함수 정의
    • 소수를 임의의 자릿수로 반올림합니다.
    • 정수를 임의의 자릿수로 반올림
    • 참고: 음수 값의 경우

위에서 언급했듯이 내장 함수 round는 일반적인 반올림이 아니라 짝수로 반올림한다는 점에 유의하십시오. 자세한 내용은 아래를 참조하세요.

내장 함수(예: 프로그래밍 언어):round()

Round()는 내장 함수로 제공됩니다. 모듈을 가져오지 않고 사용할 수 있습니다.

첫 번째 인수는 원래 숫자이고 두 번째 인수는 자릿수(반올림할 자릿수)입니다.

소수를 임의의 자릿수로 반올림합니다.

다음은 부동소수점 float 타입에 대한 처리 예이다.

두 번째 인수가 생략되면 정수로 반올림됩니다. 유형도 정수 int 유형이 됩니다.

f = 123.456

print(round(f))
# 123

print(type(round(f)))
# <class 'int'>

두 번째 인수가 지정되면 부동 소수점 부동 소수점 유형을 반환합니다.

양의 정수가 지정되면 소수점 이하 자릿수가 지정됩니다. 음의 정수가 지정되면 정수 자리가 지정됩니다. -1은 가장 가까운 10분의 1로 반올림하고 -2는 가장 가까운 100분의 1로 반올림하고 0은 정수(첫 번째 자리)로 반올림하지만 생략된 경우와 달리 부동 소수점 유형을 반환합니다.

print(round(f, 1))
# 123.5

print(round(f, 2))
# 123.46

print(round(f, -1))
# 120.0

print(round(f, -2))
# 100.0

print(round(f, 0))
# 123.0

print(type(round(f, 0)))
# <class 'float'>

정수를 임의의 자릿수로 반올림합니다.

다음은 정수 int형에 대한 처리 예이다.

두 번째 인수를 생략하거나 0 또는 양의 정수를 지정하면 원래 값이 있는 그대로 반환됩니다. 음의 정수가 지정되면 해당 정수 숫자로 반올림됩니다. 두 경우 모두 정수 int 유형이 반환됩니다.

i = 99518

print(round(i))
# 99518

print(round(i, 2))
# 99518

print(round(i, -1))
# 99520

print(round(i, -2))
# 99500

print(round(i, -3))
# 100000

round()는 일반적인 반올림이 아닌 짝수로 반올림합니다.

Python 3의 내장 round() 함수로 반올림하면 일반 반올림이 아니라 짝수로 반올림됩니다.

공식 문서에 쓰여진 것처럼 0.5는 0으로 반올림되고 5는 0으로 반올림되는 식입니다.

print('0.4 =>', round(0.4))
print('0.5 =>', round(0.5))
print('0.6 =>', round(0.6))
# 0.4 => 0
# 0.5 => 0
# 0.6 => 1

print('4 =>', round(4, -1))
print('5 =>', round(5, -1))
print('6 =>', round(6, -1))
# 4 => 0
# 5 => 0
# 6 => 10

짝수로 반올림하는 정의는 다음과 같습니다.

분수가 0.5보다 작으면 반올림합니다. 분수가 0.5보다 크면 반올림합니다. 분수가 정확히 0.5이면 반올림과 반올림 사이의 짝수로 반올림합니다.
Rounding – Wikipedia

0.5가 항상 잘리는 것은 아닙니다.

print('0.5 =>', round(0.5))
print('1.5 =>', round(1.5))
print('2.5 =>', round(2.5))
print('3.5 =>', round(3.5))
print('4.5 =>', round(4.5))
# 0.5 => 0
# 1.5 => 2
# 2.5 => 2
# 3.5 => 4
# 4.5 => 4

어떤 경우에는 짝수로 반올림하는 정의가 소수점 이하 두 자리 이후의 처리에도 적용되지 않습니다.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

이는 공식 문서에 명시된 것처럼 소수를 부동 소수점 숫자로 정확하게 표현할 수 없기 때문입니다.

부동 소수점 숫자에 대한 round()의 동작은 당신을 놀라게 할 수 있습니다:예를 들어 round(2.675, 2)는 예상대로 2.68 대신 2.67을 제공합니다. 이것은 버그가 아닙니다.:이것은 대부분의 소수를 부동 소수점 숫자로 정확하게 표현할 수 없다는 사실의 결과입니다.
round() — Built-in Functions — Python 3.10.2 Documentation

일반 반올림 또는 소수를 짝수로 정확하게 반올림하려는 경우 표준 라이브러리 소수 양자화(아래 설명)를 사용하거나 새 함수를 정의할 수 있습니다.

또한 Python 2의 round()는 짝수로 반올림되지 않고 반올림됩니다.

표준 라이브러리 십진수의 quantize()

표준 라이브러리의 10진수 모듈은 정확한 10진수 부동 소수점 숫자를 처리하는 데 사용할 수 있습니다.

decimal 모듈의 quantize() 메서드를 사용하면 반올림 모드를 지정하여 숫자를 반올림할 수 있습니다.

quantize() 메서드의 인수 반올림에 대한 설정 값은 각각 다음과 같은 의미를 갖습니다.

  • ROUND_HALF_UP:일반 반올림
  • ROUND_HALF_EVEN:짝수로 반올림

decimal 모듈은 표준 라이브러리이므로 추가 설치가 필요하지 않지만 가져오기는 필요합니다.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Decimal 객체 생성

Decimal()을 사용하여 Decimal 유형의 객체를 생성할 수 있습니다.

float 유형을 인수로 지정하면 값이 실제로 처리되는 것을 볼 수 있습니다.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

print(type(Decimal(0.05)))
# <class 'decimal.Decimal'>

예에서 볼 수 있듯이 0.05는 정확히 0.05로 취급되지 않습니다. 이것이 위에서 설명한 내장 함수 round()가 예제에서 0.05를 포함한 소수 값에 대해 예상한 것과 다른 값으로 반올림된 이유입니다.

0.5는 1/2(-1의 2제곱)이므로 정확히 이진법으로 표현할 수 있습니다.

print(Decimal(0.5))
# 0.5

float형이 아닌 string형을 str로 지정하면 정확한 값의 Decimal형으로 처리됩니다.

print(Decimal('0.05'))
# 0.05

소수를 임의의 자릿수로 반올림하고 짝수로 반올림

Decimal 유형의 객체에서 quantize()를 호출하여 값을 반올림합니다.

quantize()의 첫 번째 인수는 ‘0.1’ 또는 ‘0.01’과 같이 찾고자 하는 자릿수와 동일한 자릿수를 가진 문자열입니다.

또한 ROUNDING 인수는 반올림 모드를 지정합니다. ROUND_HALF_UP이 지정되면 일반 반올림이 사용됩니다.

f = 123.456

print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 123

print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP))
# 123.5

print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 123.46

내장 함수 round()와 달리 0.5는 1로 반올림됩니다.

print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 0.4 => 0
# 0.5 => 1
# 0.6 => 1

반올림 인수가 ROUND_HALF_EVEN으로 설정되면 내장 함수 round()와 같이 짝수로 반올림됩니다.

위에서 언급한 바와 같이 Decimal()의 인수로 부동소수점 float형을 지정하면 float형의 실제 값과 동일한 값을 갖는 Decimal 객체로 취급되므로 quantize()를 사용한 결과 메서드는 내장 함수 round()와 마찬가지로 예상한 것과 다릅니다.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Decimal()의 인수가 str 유형의 문자열로 지정되면 정확히 해당 값의 Decimal 객체로 처리되므로 결과는 예상한 대로입니다.

print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.0
# 0.15 => 0.2
# 0.25 => 0.2
# 0.35 => 0.4
# 0.45 => 0.4

0.5는 float형으로 정확하게 처리할 수 있으므로 정수로 반올림할 때 Decimal()의 인수로 float형을 지정해도 문제가 없지만 소수점 이하 자릿수로 반올림할 때는 string형 str 형식을 지정하는 것이 더 안전하다.

예를 들어, 2.675는 실제로 float 유형에서 2.67499….입니다. 따라서 소수점 이하 두 자리로 반올림하려면 문자열을 Decimal()로 지정해야 합니다. 그렇지 않으면 가장 가까운 정수(ROUND_HALF_UP) 또는 짝수(ROUND_HALF_EVEN)로 반올림하든 결과가 예상 결과와 다릅니다. ).

print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.68

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.68

quantize() 메서드는 Decimal 형식 숫자를 반환하므로 float 형식 숫자에 대해 작업하려면 float()를 사용하여 float 형식으로 변환해야 합니다. 그렇지 않으면 오류가 발생합니다.

d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

print(d)
# 123.46

print(type(d))
# <class 'decimal.Decimal'>

# print(1.2 + d)
# TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

print(1.2 + float(d))
# 124.66

정수를 임의의 자릿수로 반올림하고 짝수로 반올림

정수로 반올림하려는 경우 첫 번째 인수로 ’10’과 같은 값을 지정하면 원하는 결과를 얻을 수 없습니다.

i = 99518

print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP))
# 99518

quantize()는 Decimal 객체의 지수에 따라 반올림하지만 Decimal(’10’)의 지수는 1이 아닌 0이기 때문입니다.

E를 지수 문자열로 사용하여 임의의 지수를 지정할 수 있습니다(예: ‘1E1’). 지수 지수는 as_tuple 메소드에서 확인할 수 있습니다.

print(Decimal('10').as_tuple())
# DecimalTuple(sign=0, digits=(1, 0), exponent=0)

print(Decimal('1E1').as_tuple())
# DecimalTuple(sign=0, digits=(1,), exponent=1)

그대로 E를 사용한 지수 표기법으로 결과가 나오게 됩니다. 일반 표기법을 사용하고 싶거나, 반올림 후 정수 int형으로 연산을 하고 싶다면 int()를 사용하여 결과를 변환합니다.

print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))
# 9.952E+4

print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 99520

print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP)))
# 99500

print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP)))
# 100000

인수 반올림이 ROUND_HALF_UP으로 설정되면 일반 반올림이 발생합니다. 예를 들어 5는 10으로 반올림됩니다.

print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 4 => 0
# 5 => 10
# 6 => 10

물론 문자열로 지정하면 문제 없습니다.

새 함수 정의

decimal 모듈을 사용하는 방법은 정확하고 안전하지만 형식 변환에 익숙하지 않은 경우 일반적인 반올림을 수행하기 위해 새 함수를 정의할 수 있습니다.

예를 들어 다음 기능과 같이 이를 수행할 수 있는 많은 방법이 있습니다.

def my_round(val, digit=0):
    p = 10 ** digit
    return (val * p * 2 + 1) // 2 / p

자릿수를 지정할 필요가 없고 항상 소수점 첫째 자리로 반올림하려면 더 간단한 형식을 사용할 수 있습니다.

my_round_int = lambda x: int((x * 2 + 1) // 2)

정확해야 하는 경우 소수를 사용하는 것이 더 안전합니다.

다음은 참고용입니다.

소수를 임의의 자릿수로 반올림합니다.

print(int(my_round(f)))
# 123

print(my_round_int(f))
# 123

print(my_round(f, 1))
# 123.5

print(my_round(f, 2))
# 123.46

라운드와 달리 일반 반올림에 따라 0.5가 1이 됩니다.

print(int(my_round(0.4)))
print(int(my_round(0.5)))
print(int(my_round(0.6)))
# 0
# 1
# 1

정수를 임의의 자릿수로 반올림

i = 99518

print(int(my_round(i, -1)))
# 99520

print(int(my_round(i, -2)))
# 99500

print(int(my_round(i, -3)))
# 100000

라운드와 달리 5는 일반적인 반올림에 따라 10이 됩니다.

print(int(my_round(4, -1)))
print(int(my_round(5, -1)))
print(int(my_round(6, -1)))
# 0
# 10
# 10

참고: 음수 값의 경우

위의 예제 함수에서 -0.5는 0으로 반올림됩니다.

print(int(my_round(-0.4)))
print(int(my_round(-0.5)))
print(int(my_round(-0.6)))
# 0
# 0
# -1

음수 값을 반올림하는 방법은 여러 가지가 있지만 -0.5를 -1로 만들고 싶다면 다음과 같이 수정하면 됩니다. 예를 들면

import math

def my_round2(val, digit=0):
    p = 10 ** digit
    s = math.copysign(1, val)
    return (s * val * p * 2 + 1) // 2 / p * s

print(int(my_round2(-0.4)))
print(int(my_round2(-0.5)))
print(int(my_round2(-0.6)))
# 0
# -1
# -1
Copied title and URL