파이썬 3.x 반올림 동작


176

방금 Python 3.0의 새로운 기능을 읽고 있었고 다음 과 같이 말합니다.

round () 함수 반올림 전략 및 리턴 유형이 변경되었습니다. 정확한 중간 사례는 이제 0이 아닌 가장 가까운 짝수로 반올림됩니다. 예를 들어 round (2.5)는 이제 3이 아니라 2를 반환합니다.

그리고 round에 대한 문서 :

round ()를 지원하는 내장 유형의 경우, 값은 10의 거듭 제곱 n에 가장 가까운 배수로 반올림됩니다. 두 배수가 동일하게 가까운 경우 고른 선택을 향해 반올림됩니다.

따라서 v2.7.3에서 :

In [85]: round(2.5)
Out[85]: 3.0

In [86]: round(3.5)
Out[86]: 4.0

내가 예상했던대로. 그러나 현재 v3.2.3에서 :

In [32]: round(2.5)
Out[32]: 2

In [33]: round(3.5)
Out[33]: 4

이것은 반 직관적이며 반올림에 대해 이해하는 것과 반대되는 것으로 보입니다 (그리고 사람들을 트립 할 수밖에 없습니다). 영어는 모국어가 아니지만이 글을 읽을 때까지 반올림의 의미를 알고 있다고 생각했습니다. 내 검색.

  1. 왜 이것이 이것이 변경되었는지에 대한 통찰력이 있습니까?
  2. 이러한 종류의 (일관되지 않은) 반올림을 수행 하는 다른 주류 프로그래밍 언어 (예 : C, C ++, Java, Perl, ..)가 있습니까?

내가 여기서 무엇을 놓치고 있습니까?

업데이트 : @ Li-aungYip의 "Banker 's rounding"에 대한 의견은 검색 할 올바른 검색어 / 키워드를 제공 했으며이 SO 질문을 발견했습니다 . 그래서 나는 그것을주의 깊게 읽을 것입니다.


27
나는 이것을 찾아 볼 시간이 없지만 이것을 "뱅커의 반올림"이라고 믿습니다. 나는 그것이 금융 산업에서 일반적이라고 생각합니다.
Li-aung Yip

2
@ sberry 글쎄, 그 행동은 자체 설명과 일치합니다. 이 말을 그래서 경우 "반올림"값을 두 배로하고 그것을했다, 또한 :) 일관 될 것이다 ..하지만 일반적으로 반올림 것과는 달리 보인다 수단을 . 더 나은 이해를 찾고 있습니다.
Levon

1
@ Li-aungYip 리드 리 "뱅커의 반올림"에 감사드립니다 .. 내가 찾아 볼 것입니다.
Levon


3
참고 사항 : 은행 반올림은 금융 분야에서만 일반적이지 않습니다. 이것은 70 년대 초에 이미 초등학교에서 반올림되는 방법입니다 :
Lennart Regebro

답변:


160

파이썬 3.0의 방식은 요즘 표준 반올림 방법으로 간주되지만 일부 언어 구현은 아직 버스에 없습니다.

간단한 "항상 반올림"기술은 더 높은 숫자를 향한 약간의 편견입니다. 많은 수의 계산을 사용하면 이것이 중요 할 수 있습니다. Python 3.0 접근 방식은이 문제를 해결합니다.

일반적으로 사용되는 반올림 방법은 여러 가지가 있습니다. 부동 소수점 수학에 대한 국제 표준 인 IEEE 754는 다섯 가지 다른 반올림 방법을 정의 합니다 (Python 3.0에서 사용하는 방법 이 기본값 임). 그리고 다른 것들도 있습니다.

이 동작은 예상만큼 널리 알려지지 않았습니다. 내가 올바르게 기억한다면, AppleScript는이 반올림 방법의 초기 채택 자였습니다. roundAppleScript 의 명령은 실제로 몇 가지 옵션을 제공하지만 IEEE 754와 마찬가지로 기본적으로 반올림입니다.이 round명령 을 구현 한 엔지니어 는 "내가 배운 것처럼 작동하도록 모든 요청에 부딪 쳤 습니다. "그것을 구현했습니다. round 2.5 rounding as taught in school유효한 AppleScript 명령입니다. :-)


4
나는이 "기본 요즘 반올림 방법을 거의 보편적으로 알고있다"는 것을 알지 못했다. C / C ++ / Java / Perl 또는 다른 "주류"언어가 같은 방식으로 반올림을 구현하고 있는지 아는가?
Levon

3
루비는 그렇게합니다. Microsoft의 .NET 언어는 그렇게합니다. 그러나 Java는 나타나지 않습니다. 가능한 모든 언어로 추적 할 수는 없지만 최근에 디자인 된 언어에서 가장 일반적이라고 생각합니다. 나는 C와 C ++이 그렇지 않다고 생각합니다.
kindall

5
ruby는 다음 3을 반환 합니다2.5.round
jfs

14
"이전"비헤이비어가 구현되는 비꼬는 방식을 좋아하기 때문에 AppleScript의 처리에 대해 약간 추가했습니다.
kindall

2
@kindall이 방법은 1985 년 이후 IEEE 기본 반올림 모드였습니다 (IEEE 754-1985이 게시 된 시점). C99 (및 C ++ )부터는 C에서 기본 반올림 모드 였지만 C99 (및 그 이전에 산발적으로 지원되는 C ++ 11)를 사용하는 "round ()"함수를 사용할 수 있었기 때문에 대신 0에서 반올림합니다. 내부 부동 소수점 반올림 및 rint () 함수 계열은 여전히 ​​반올림 모드 설정을 따르며, 기본적으로 반올림은 짝수로 설정됩니다.
Wlerin

41

Decimal 모듈을 사용하여 Py3000에서 반올림을 제어 할 수 있습니다 .

>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'), 
    rounding=decimal.ROUND_HALF_UP)
>>> Decimal('4')

>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'),    
    rounding=decimal.ROUND_HALF_EVEN)
>>> Decimal('2')

>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'), 
    rounding=decimal.ROUND_HALF_DOWN)
>>> Decimal('3')

감사합니다 ..이 모듈에 익숙하지 않았습니다. 파이썬 v 2.x의 동작을 어떻게 얻습니까? 당신이 보여주는 예제는 그렇게하지 않는 것 같습니다. 가능하다면 궁금합니다.
Levon

1
@Levon : 상수 ROUND_HALF_UP는 Python 2.X의 이전 동작과 같습니다.
dawg

2
암시 적으로이를 수행하는 Decimal 모듈에 대한 컨텍스트를 설정할 수도 있습니다. setcontext()기능을 참조하십시오 .
kindall

이것이 바로 오늘 내가 찾던 것입니다. 파이썬 3.4.3에서 예상대로 작동합니다. 또한 돈과 같이 가장 가까운 100으로 반올림하려는 경우 로 변경 quantize(decimal.Decimal('1')하여 반올림하는 정도를 제어 할 수 있습니다 quantize(decimal.Decimal('0.00').
Igor

이 솔루션을 대체 작동 round(number, ndigits)만큼으로 ndigits긍정적이지만, 귀찮게 당신이 뭔가를 교체 할 수는 없습니다 round(5, -1).
Pekka Klärck 2016 년

15

문서에서 중요한 메모를 여기에 추가하십시오.

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

노트

float에 대한 round ()의 동작은 놀랍습니다. 예를 들어 round (2.675, 2)는 예상 2.68 대신 2.67을 제공합니다. 이것은 버그가 아닙니다. 대부분의 소수는 정확히 부동 소수점으로 표현할 수 없다는 사실의 결과입니다. 자세한 내용은 부동 소수점 산술 : 문제 및 제한 사항을 참조하십시오.

따라서 Python 3.2에서 다음과 같은 결과를 얻는 것에 놀라지 마십시오.

>>> round(0.25,1), round(0.35,1), round(0.45,1), round(0.55,1)
(0.2, 0.3, 0.5, 0.6)

>>> round(0.025,2), round(0.035,2), round(0.045,2), round(0.055,2)
(0.03, 0.04, 0.04, 0.06)

나는 것을보고. 그리고 첫 번째 반응은 : "2.67x"의 모든 순열을 표현할 수없는 16 비트 CPU를 누가 사용하고 있습니까? 분수를 부동 소수점으로 표현할 수 없다고 말하는 것은 여기에 희생양처럼 보입니다 : 현대의 CPU는 어떤 언어에서도 (파이썬 제외) 정확하지 않습니다.
Adam

9
@ 아담 : 당신이 오해하는 것 같아요. float를 저장하는 데 사용되는 이진 형식 (IEEE 754 binary64)은 2.675정확하게 표현할 수 없습니다 2.67499999999999982236431605997495353221893310546875. 컴퓨터가 얻을 수있는 가장 가까운 값은 입니다. 그것은 아주 가까이,하지만 그렇지 않아 정확히 동일한 2.675: 그것은 아주 약간 가까이 2.67보다 2.68. 따라서 round함수는 올바른 일을 수행하고 더 가까운 2 자리 후 값, 즉로 반올림합니다 2.67. 이것은 파이썬과 관련이 없으며 바이너리 부동 소수점과 관련이 있습니다.
Mark Dickinson

3
소스 코드 상수가 주어 졌기 때문에 "올바른 것"은 아니지만 요점을 알 수 있습니다.
Adam

@ 아담 : 나는 JS 에서이 같은 기발함에 부딪 쳤으므로 언어마다 다릅니다.
Igor

5

나는 최근에도 이것에 문제가 있었다. 따라서이 문제를 해결하고 동일한 반올림 동작을 제공하는 2 개의 함수 trueround () 및 trueround_precision ()을 가진 python 3 모듈을 개발했습니다 (은행가의 반올림이 아닌). 다음은 모듈입니다. 코드를 저장하고 복사하거나 가져 오기만하면됩니다. 참고 : trueround_precision 모듈은 ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP 및 ROUND_05UP 플래그에 따라 필요에 따라 반올림 동작을 변경할 수 있습니다 (자세한 내용은 해당 모듈 설명서 참조). 아래 함수에 대해서는 docstring을 참조하거나 추가 문서를 위해 인터프리터에 복사 된 경우 help (trueround) 및 help (trueround_precision)를 사용하십시오.

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

def trueround(number, places=0):
    '''
    trueround(number, places)

    example:

        >>> trueround(2.55, 1) == 2.6
        True

    uses standard functions with no import to give "normal" behavior to 
    rounding so that trueround(2.5) == 3, trueround(3.5) == 4, 
    trueround(4.5) == 5, etc. Use with caution, however. This still has 
    the same problem with floating point math. The return object will 
    be type int if places=0 or a float if places=>1.

    number is the floating point number needed rounding

    places is the number of decimal places to round to with '0' as the
        default which will actually return our interger. Otherwise, a
        floating point will be returned to the given decimal place.

    Note:   Use trueround_precision() if true precision with
            floats is needed

    GPL 2.0
    copywrite by Narnie Harshoe <signupnarnie@gmail.com>
    '''
    place = 10**(places)
    rounded = (int(number*place + 0.5if number>=0 else -0.5))/place
    if rounded == int(rounded):
        rounded = int(rounded)
    return rounded

def trueround_precision(number, places=0, rounding=None):
    '''
    trueround_precision(number, places, rounding=ROUND_HALF_UP)

    Uses true precision for floating numbers using the 'decimal' module in
    python and assumes the module has already been imported before calling
    this function. The return object is of type Decimal.

    All rounding options are available from the decimal module including 
    ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, 
    ROUND_HALF_UP, ROUND_UP, and ROUND_05UP.

    examples:

        >>> trueround(2.5, 0) == Decimal('3')
        True
        >>> trueround(2.5, 0, ROUND_DOWN) == Decimal('2')
        True

    number is a floating point number or a string type containing a number on 
        on which to be acted.

    places is the number of decimal places to round to with '0' as the default.

    Note:   if type float is passed as the first argument to the function, it
            will first be converted to a str type for correct rounding.

    GPL 2.0
    copywrite by Narnie Harshoe <signupnarnie@gmail.com>
    '''
    from decimal import Decimal as dec
    from decimal import ROUND_HALF_UP
    from decimal import ROUND_CEILING
    from decimal import ROUND_DOWN
    from decimal import ROUND_FLOOR
    from decimal import ROUND_HALF_DOWN
    from decimal import ROUND_HALF_EVEN
    from decimal import ROUND_UP
    from decimal import ROUND_05UP

    if type(number) == type(float()):
        number = str(number)
    if rounding == None:
        rounding = ROUND_HALF_UP
    place = '1.'
    for i in range(places):
        place = ''.join([place, '0'])
    return dec(number).quantize(dec(place), rounding=rounding)

도움이 되었기를 바랍니다,

나르 니


5

Python 3.x는 0.5 값을 이웃으로 반올림합니다.

assert round(0.5) == 0
assert round(1.5) == 2
assert round(2.5) == 2

import decimal

assert decimal.Decimal('0.5').to_integral_value() == 0
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 2

그러나 필요한 경우 십진수 반올림 "뒤로"를 항상 0.5로 올림으로 변경할 수 있습니다.

decimal.getcontext().rounding = decimal.ROUND_HALF_UP

assert decimal.Decimal('0.5').to_integral_value() == 1
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 3

i = int(decimal.Decimal('2.5').to_integral_value()) # to get an int
assert i == 3
assert type(i) is int

1

Python 3의 Python 2 반올림 동작

소수점 이하 15 자리에 1을 더합니다. 최대 15 자리의 정확도.

round2=lambda x,y=None: round(x+1e-15,y)

3
이 공식의 직관을 설명해 주시겠습니까?
Hadi

2
내가 이해 한 바에 따르면, 정확하게 표현할 수없는 분수는 최대 15 9까지, 그리고 부정확합니다. 예를 들어 2.675입니다 2.67499999999999982236431605997495353221893310546875. 1e-15를 추가하면 2.675 이상이되고 올바르게 반올림됩니다. 분수가 이미 코드 상수를 초과하는 경우 1e-15를 추가하면 반올림에 아무런 변화가 없습니다.
Benoit Dufresne

1

어떤 경우에는 :

in: Decimal(75.29 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(75.29 / 2, 2)
out: 37.65 GOOD

in: Decimal(85.55 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(85.55 / 2, 2)
out: 42.77 BAD

수정 :

in: round(75.29 / 2 + 0.00001, 2)
out: 37.65 GOOD
in: round(85.55 / 2 + 0.00001, 2)
out: 42.78 GOOD

더 많은 소수 (예 : 4)를 원하면 (+ 0.0000001)을 추가해야합니다.

나를 위해 일하십시오.


이것은 나를 위해 일한 유일한 솔루션이었습니다. 게시 주셔서 감사합니다. 모두 0.5 반올림 / 내림을 의도 한 것 같으므로 십진수 반올림 문제를 관리 할 수 ​​없습니다.
Gayathri

-1

샘플 복제 :

['{} => {}'.format(x+0.5, round(x+0.5)) for x in range(10)]

['0.5 => 0', '1.5 => 2', '2.5 => 2', '3.5 => 4', '4.5 => 4', '5.5 => 6', '6.5 => 6', '7.5 => 8', '8.5 => 8', '9.5 => 10']

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

상태 :

소수점 뒤에 n 자리의 정밀도로 반올림 된 숫자를 반환합니다. ndigits가 생략되거나 None이면 입력에 가장 가까운 정수를 반환합니다.

round ()를 지원하는 내장 유형의 경우, 값은 10에서 가장 가까운 n의 제곱으로 반올림됩니다. 두 배수가 동일하게 가까운 경우 반올림은 짝수 선택으로 수행됩니다 (예를 들어, round (0.5) 및 round (-0.5)는 0이고 round (1.5)는 2). 모든 정수 값은 n 숫자 (양수, 0 또는 음수)에 유효합니다. ndigits가 생략되거나 None 인 경우 반환 값은 정수입니다. 그렇지 않으면 반환 값은 숫자와 같은 유형입니다.

일반적인 파이썬 객체 번호의 경우 델리게이트를 숫자로 반올림합니다. 라운드 .

참고 float에 대한 round ()의 동작은 놀랍습니다. 예를 들어 round (2.675, 2)는 예상 2.68 대신 2.67을 제공합니다. 이것은 버그가 아닙니다. 대부분의 소수는 정확히 부동 소수점으로 표현할 수 없다는 사실의 결과입니다. 자세한 내용은 부동 소수점 산술 : 문제 및 제한 사항을 참조하십시오.

이 통찰력이 주어지면 수학을 사용하여 해결할 수 있습니다.

import math
def my_round(i):
  f = math.floor(i)
  return f if i - f < 0.5 else f+1

이제 라운드 대신 my_round로 동일한 테스트를 실행할 수 있습니다.

['{} => {}'.format(x + 0.5, my_round(x+0.5)) for x in range(10)]
['0.5 => 1', '1.5 => 2', '2.5 => 3', '3.5 => 4', '4.5 => 5', '5.5 => 6', '6.5 => 7', '7.5 => 8', '8.5 => 9', '9.5 => 10']

-2

학교에서 가르치는 것처럼 Python 3.x에서 반올림하는 가장 쉬운 방법은 보조 변수를 사용하는 것입니다.

n = 0.1 
round(2.5 + n)

그리고 이것은 시리즈 2.0에서 3.0의 결과입니다 (0.1 단계).

>>> round(2 + n)
>>> 2

>>> round(2.1 + n)
>>> 2

>>> round(2.2 + n)
>>> 2

>>> round(2.3 + n)
>>> 2

>>> round(2.4 + n)
>>> 2

>>> round(2.5 + n)
>>> 3

>>> round(2.6 + n)
>>> 3

>>> round(2.7 + n)
>>> 3

>>> round(2.8 + n)
>>> 3

>>> round(2.9 + n)
>>> 3

>>> round(3 + n)
>>> 3

-2

math.ceil 모듈을 사용하여 반올림을 제어 할 수 있습니다.

import math
print(math.ceil(2.5))
> 3

그것은 항상 소수 부분이없는 숫자를 반환합니다. 이것은 반올림되지 않습니다. CEIL (2.5) = 2, CEIL (2.99) = 2
krafter

1
python3 +에서 숫자 인수가 양수 또는 음수이면 ceil 함수는 상한값을 반환합니다.
Eds_k

[14]에서 : math.ceil (2.99) Out [14] : 3
Eds_k

네, 제가 잘못해서 죄송합니다. Ceil ()은 천장 값을 반환하지만 floor ()는 내가 말한 것을 반환합니다. 그러나 여전히 내 의견으로는 이것은 반올림 동작이 아닙니다 (이 두 기능 모두)
krafter

-4

이 코드를 사용해보십시오 :

def roundup(input):   
   demo = input  if str(input)[-1] != "5" else str(input).replace("5","6")
   place = len(demo.split(".")[1])-1
   return(round(float(demo),place))

결과는 다음과 같습니다.

>>> x = roundup(2.5)
>>> x
3.0  
>>> x = roundup(2.05)
>>> x
2.1 
>>> x = roundup(2.005)
>>> x
2.01 

출력은 여기에서 확인할 수 있습니다 : https://i.stack.imgur.com/QQUkS.png

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.