programing

Python 3.x 반올림 동작

shortcode 2022. 10. 26. 23:13
반응형

Python 3.x 반올림 동작

방금 What's New In Python 3.0을 다시 읽었는데 다음과 같이 되어 있습니다.

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

및 라운드용 문서:

라운드()를 지원하는 삽입형에서는 값이 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

이것은 반직관적이고 반올림에 대해 제가 이해하고 있는 것과 반대되는 것 같습니다(그리고 사람들을 혼란스럽게 합니다).영어가 모국어는 아니지만, 이 글을 읽기 전까지는 반올림의 의미를 알고 있었다:-/ v3가 소개되었을 때, 이것에 대한 논의가 있었을 것이라고 확신하지만, 검색에서는 좋은 이유를 찾을 수 없었다.

  1. 이게 왜 이렇게 바뀌었는지 아는 사람 있나요?
  2. (나에게) 이런 종류의 반올림을 하는 다른 주류 프로그래밍 언어(예를 들어 C, C++, Java, Perl 등)가 있습니까?

내가 뭘 놓쳤지?

업데이트: @Li-aungYip의 코멘트 re "Banker's Rounding"을 통해 검색해야 할 올바른 검색어/키워드를 얻을 수 있었습니다.그리고 다음 질문을 찾았습니다.의 이유는 무엇입니까?NET에서는 뱅커 라운딩을 디폴트로 하고 있기 때문에, 그것을 주의 깊게 읽어 보겠습니다.

Python 3의 방식('반올림' 또는 '뱅커 반올림'이라고 함)은 현재 표준 반올림 방식으로 간주되고 있지만, 일부 언어 구현은 아직 버스에 제공되지 않았다.

단순한 「항상 반올림 0.5 」테크놀로지에 의해, 높은 수치로 약간 치우치게 됩니다.계산 횟수가 많을 경우, 이는 매우 중요할 수 있습니다.Python 3.0 접근법은 이 문제를 해결합니다.

일반적으로 사용되는 반올림 방법에는 여러 가지가 있습니다.부동소수점 산술의 국제 표준인 IEEE 754는 5개의 다른 반올림 방법(Python 3.0에서 사용되는 방법이 기본값)을 정의합니다.그리고 다른 사람들도 있어요.

이런 행동은 그렇게 널리 알려져 있지 않다.AppleScript는, 제 기억이 맞다면, 이 반올림 방식의 얼리 어답터였습니다.AppleScript의 명령어는 몇 가지 옵션을 제공하지만 IEEE 754에서는 round-toward-even이 기본입니다.보아하니 이 시스템을 구현한 엔지니어는round커맨드는, 「학교에서 배운 대로 동작하게 해 주세요」라고 하는 모든 요구에 싫증이 나서, 다음과 같이 실장했습니다.round 2.5 rounding as taught in school는 유효한 AppleScript 명령어입니다. :- )

10진수 모듈을 사용하여 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')

설명서에서 중요한 메모를 여기에 추가합니다.

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

메모

플로트에 대한 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)

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

단, 필요에 따라 소수 반올림 "back"을 항상 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

저도 최근에 이런 문제가 있었어요.그 때문에, 이것을 다루고, 같은 반올림 동작을 하는 2개의 함수를 가지는 python 3 모듈을 개발했습니다(뱅크 라운딩이 아닙니다).여기 모듈이 있습니다.코드를 저장하고 복사하거나 가져오기만 하면 됩니다.주의: trueround_precision 모듈은 10진수 모듈의 ROUND_CEILING, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_05UP 플래그에 따라 필요에 따라 반올림 동작을 변경할 수 있습니다(자세한 내용은 해당 모듈 참조).다음 함수에 대해서는 문서스트링을 참조해 주십시오.또한 설명서가 필요한 경우 도움말(트루라운드)과 도움말(트루라운드_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)

이게 도움이 됐으면 좋겠는데

나리

파이썬 3의 파이썬 2 반올림 동작.

소수점 15번째 자릿수에 1을 더합니다.최대 15자리 정확도

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

경우에 따라서는:

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.00001)을 추가해야 합니다.

날 위해 일해.

샘플 재생:

['{} => {}'.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일 경우 입력에 가장 가까운 정수를 반환합니다.

삽입형 지지 라운드()의 경우, 값은 10의 제곱에서 n자릿수를 뺀 배수에 가장 가까운 값으로 반올림됩니다.두 배수가 동등하게 가까우면 짝수 선택을 향해 반올림됩니다(예를 들어 라운드(0.5)와 라운드(-0.5)는 모두 0, 라운드(1.5)는 2).임의의 정수 값은 n자리(양수, 0 또는 음수)에 대해 유효합니다.ndigits가 생략되거나 None일 경우 반환값은 정수입니다.그렇지 않은 경우 반환값은 번호와 동일한 유형을 가집니다.

일반적인 Python 개체 번호의 경우 number.round로 반올림합니다.

주의: 플로트에 대한 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']
# round module within numpy when decimal is X.5 will give desired (X+1)

import numpy as np
example_of_some_variable = 3.5
rounded_result_of_variable = np.round(example_of_some_variable,0)
print (rounded_result_of_variable)

다음 코드를 사용해 보십시오.

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 에서 확인할 수 있습니다.

학교에서 배운 것처럼 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

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

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

언급URL : https://stackoverflow.com/questions/10825926/python-3-x-rounding-behavior

반응형