Вопрос-Ответ

Python 3.x rounding behavior

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

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


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


и документация для round:


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


Итак, в версии 7.3:

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

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

как я и ожидал. Однако теперь в версии 2.3:

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

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

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


  1. У кого-нибудь есть представление о том, почему это было изменено на this?

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

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

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

Переведено автоматически
Ответ 1

Способ Python 3 (называемый "округлять наполовину до четного" или "банковское округление") в наши дни считается стандартным методом округления, хотя некоторые языковые реализации еще не запущены.

Простой метод "всегда округлять 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.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.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
2023-09-09 19:30 python python-3.x