Python 3.х округления поведения


Я просто перечитываю Что нового в Python 3.0 и в нем говорится:

стратегия округления функции round () и тип возвращаемого значения изменились. Точные случаи на полпути теперь округляются до ближайшего четного результата вместо этого далеко от нуля. (Например, round (2.5) теперь возвращает 2, а не 3.)

и документация для круглые:

для встроенных типов, поддерживающих round (), значения являются округленное до ближайшего кратного 10 в степени минус N; если два кратных одинаково близко, округление делается в сторону четного выбора

Итак, под П2.7.3:

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

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

Как я и ожидал. Однако теперь под В3.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 "округление банкира" дал мне правильный поисковый запрос / ключевые слова для поиска, и я нашел этот вопрос:почему .NET использует округление банкира по умолчанию?, так что я буду читать это внимательно.

6 112

6 ответов:

путь Python 3.0 считается стандартным методом округления в эти дни, хотя некоторые языковые реализации еще не находятся на шине.

простой метод "всегда округляет 0,5 вверх" приводит к небольшому смещению в сторону более высокого числа. При большом количестве вычислений это может быть значительным. Подход Python 3.0 устраняет эту проблему.

существует более одного метода округления в общем использовании. IEEE 754, международный стандарт для математики с плавающей запятой, определяет пять различных методов округления (по умолчанию используется Python 3.0). А есть и другие.

это поведение не так широко известно, как должно быть. AppleScript был, если я правильно помню, ранним последователем этого метода округления. Элемент round команда в AppleScript на самом деле предлагает несколько вариантов, но round-towards-even является значением по умолчанию, как и в IEEE 754. По-видимому, инженер, который реализовал round команда так надоела всем просьбы "заставить его работать так, как я узнал в школе", что он реализовал именно это: round 2.5 rounding as taught in school является допустимой командой AppleScript. : -)

вы можете контролировать округление вы получаете в 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() для поплавков может быть удивительным: например, раунд (2.675, 2) дает 2.67 вместо ожидаемых 2.68. Это не ошибка: это результат того, что большинство десятичных дробей не может быть представлен точно как поплавок. См. Раздел Арифметика С Плавающей Запятой: Проблемы и Ограничения для получения дополнительной информации.

Так что не удивляйтесь, чтобы получить следующие результаты в 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, который имеет 2 функции trueround() и trueround_precision (), которые обращаются к этому и дают такое же поведение округления, которое используется в начальной школе (не округление банкира). Вот этот модуль. Просто сохраните код и скопируйте его или импортируйте. Примечание: модуль trueround_precision может изменять поведение округления в зависимости от потребностей в соответствии с ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, Флаги ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP и ROUND_05UP в десятичном модуле (дополнительную информацию см. В документации по этим модулям). Для функций ниже см. docstrings или используйте 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)

надеюсь, это поможет,

Narnie

поведение округления Python 2 в python 3.

добавление 1 в 15 десятичных знаков. Точность до 15 цифр.

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

Python 3.х раундов .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

однако можно изменить десятичное округление "назад" на всегда круглое .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