Поведение округления Python 3.x

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

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

и документация для 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

Это кажется противоречивым и противоречащим тому, что я понимаю о округление (и связанное с поездкой людей). Английский не мой родной язык, но пока я не прочитал это, я думал, что знаю, что такое округление: -/Я уверен в момент введения v3 должно было быть некоторое обсуждение это, но я не смог найти вескую причину в своем поиске.

  • Кто-нибудь знает, почему это было изменено на это?
  • Существуют ли какие-либо другие основные языки программирования (например, C, С++, Java, Perl,..), которые делают это (для меня непоследовательное) округление?

Что мне здесь не хватает?

UPDATE: @Li-aungYip comment re "Банкир округление" дал мне правильный поиск/ключевые слова для поиска, и я нашел этот вопрос SO: Почему .NET использует округление банкира как по умолчанию?, поэтому я буду внимательно его читать.

Ответы

Ответ 1

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

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

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

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

Ответ 2

Вы можете управлять округлением, которое вы получаете в 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')

Ответ 3

Просто добавлю сюда важное примечание из документации:

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

Заметка

Поведение 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)

Ответ 4

У меня недавно были проблемы с этим. Следовательно, я разработал модуль 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 в десятичном модуле (дополнительную информацию см. В документации модулей). Для нижеприведенных функций см. Документацию к документам или используйте 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 <[email protected]>
    '''
    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 <[email protected]>
    '''
    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

Ответ 5

Python 3.x округляет .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

Ответ 6

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

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

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

Ответ 7

Некоторые случаи:

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).

Работа для меня.

Ответ 8

Образец репродукции:

['{} => {}'.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

Состояния:

Возвращаемое число, округленное до точности ndigits после десятичной точки. Если ndigits опущен или равен None, он возвращает ближайшее целое число на свой вход.

Для встроенных типов, поддерживающих round(), значения округляются до ближайшего кратного 10 к степени минус ndigits; если два мультипликатора одинаково близки, округление выполняется до четного выбора (например, как раунд (0.5), так и раунд (-0.5) равны 0, а раунд (1.5) равен 2). Любое целочисленное значение допустимо для ndigits (положительное, нулевое или отрицательное). Возвращаемое значение является целым числом, если ndigits опущено или отсутствует. В противном случае возвращаемое значение имеет тот же тип, что и число.

Для общего номера объекта Python округлите делегатов до числа. круглый

Замечание Поведение round() для чисел с плавающей точкой может быть удивительным: например, round (2.675, 2) дает 2,67 вместо ожидаемых 2,68. Это не ошибка: это результат того факта, что большинство десятичных дробей не могут быть представлены именно как число с плавающей точкой. Посмотрите Арифметику с плавающей запятой: Проблемы и Ограничения для получения дополнительной информации.

Учитывая это понимание, вы можете использовать некоторую математику, чтобы решить ее

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

теперь вы можете запустить тот же тест с my_round вместо 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']

Ответ 9

Круглый оператор округляет значение до ближайшего целого значения.

Например:

Если значение больше o.5, то оно округляется до 1

print(round(211.5554, 2)) // output is 211.56

Если значение меньше 0,5, оно округляется до 0

print(round(211.5544, 2)) // output is 211.55