Изменение встроенной в Python функции round() между 2.4 и 2.7

Изменилась ли встроенная функция round() в Python между 2.4 и 2.7?

Питон 2.4:

Python 2.4.6 (#1, Feb 12 2009, 14:52:44)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = 1480.39499999999998181010596454143524169921875
>>> round(f,2)
1480.4000000000001
>>>

Питон 2.7:

Python 2.7.1 (r271:86832, May 13 2011, 08:14:41)
[GCC 3.4.6 20060404 (Red Hat 3.4.6-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = 1480.39499999999998181010596454143524169921875
>>> round(f, 2)
1480.39
>>>

Есть ли способ вернуть поведение Python 2.4?

Я знаю, что правильный ответ, конечно, использовать модуль десятичной арифметики. К сожалению, это, вероятно, не вариант на данный момент, учитывая ограничения по времени.

Обновить

Для контекста я сравниваю значения в двух системах, одна из которых использует десятичное представление, а другая - с плавающей запятой. Это может (или не может) быть законным различием между системами, которое необходимо проверить в дальнейшем, поэтому я буду консультироваться с пользователями и разбираться с этим на уровне «отчетности», а не в момент получения данных из систем. .

Спасибо за вашу помощь!


person Paul Johnson    schedule 20.12.2011    source источник
comment
Вы предоставляете гораздо больше цифр, чем может обработать внутреннее представление - возможно, разница заключается в интерпретации ввода, а не в round.   -  person Mark Ransom    schedule 21.12.2011
comment
P.S. Ответ Python 2.7 кажется мне более правильным, я бы не ожидал найти вариант для получения неверных результатов.   -  person Mark Ransom    schedule 21.12.2011
comment
Unfortunately, this [decimal module] probably isn't an option at this point given time limitations. Я сомневаюсь, что это действительно так, по крайней мере, вы не должны ожидать, что число с плавающей запятой даст точный результат или точное округление, поскольку число с плавающей запятой по определению является неточным. Избавьте себя от хлопот и преобразуйте свою программу в десятичную.   -  person Lie Ryan    schedule 21.12.2011
comment
@Mark: Нет, он просто пишет точное значение 1480.395.   -  person dan04    schedule 21.12.2011
comment
@ dan04: Не существует такого понятия, как точное значение 1480,395 в (двоичном) формате с плавающей запятой. Если ему действительно нужно это точное значение, он должен использовать Decimal или его эквивалент (gmpy, или вычислить свое собственное, используя целые числа и т. д.).   -  person John Y    schedule 21.12.2011
comment
@JohnY, я думаю, что @dan04 пытается сказать, что очень длинное десятичное число на самом деле является точным двоичным числом, и это двоичное число ближе всего к 1480.395. Это не отменяет моего предположения о том, что разница вызвана изменением обработки ввода, хотя другие свидетельства указывают на другое.   -  person Mark Ransom    schedule 21.12.2011
comment
@Mark: я не знаю, что вы подразумеваете под обработкой ввода или другими доказательствами. Разработчики Python (в основном Марк Дикинсон) провели довольно обширную переработку поведения с плавающей запятой в Python 2.7/3.1, в частности, улучшили repr и исправили round (исправление здесь означает относительно спецификации IEEE с плавающей запятой).   -  person John Y    schedule 21.12.2011
comment
@JohnY, примечания к выпуску 2.7/3.1 являются свидетельством того, о чем я говорю. Я просто хотел указать на возможность, не указанную в исходном вопросе, что преобразование строки в двоичное число с плавающей запятой изменилось (именно это я имею в виду под обработкой ввода). Ваш ответ уже имеет мой +1.   -  person Mark Ransom    schedule 21.12.2011
comment
@MarkRandom: Да, это то, что я имел в виду.   -  person dan04    schedule 21.12.2011


Ответы (4)


Ответ на ваш первый вопрос: да, round был исправлен в Python 2.7.

Ответ на ваш второй вопрос: я не уверен, если не считать фактического использования более раннего Python (2.6 все еще должен демонстрировать поведение 2.4). В этом конкретном случае ваше значение неотличимо от 1480,395, а 1480,395 (для нас, людей с основанием 10) округляется до 1480,40. Итак, я полагаю, вы могли бы попробовать сначала округлить до одного места за пределами того, что вам действительно нужно, превратить это в строку, а затем получить из нее десятичное число... но, извините, я не могу придумать ничего, что не связано с десятичным числом. Если я что-нибудь придумаю (до того, как кто-то другой опубликует что-то лучше), я вернусь и отредактирую.

(Но действительно ли Decimal так сложно использовать?)

Изменить. Вот пример использования десятичного числа:

>>> from decimal import Decimal
>>> f = Decimal('1480.395')
>>> f.quantize(Decimal('0.00'))
Decimal('1480.40')
>>> float(Decimal('1480.40'))
1480.4

Некоторые примечания:

Python 2.7 позволяет вам использовать числа с плавающей запятой в качестве входных данных для Decimal. Не делайте этого, так как вы вернетесь к тому, с чего начали. Кроме того, не используйте встроенную функцию round для десятичного числа, потому что эта функция преобразует ваше десятичное число обратно в число с плавающей запятой, и, таким образом, вы вернетесь к тому, с чего начали. В своей простейшей форме quantize Decimal берет другое десятичное число, которое имеет количество десятичных разрядов, которое вам действительно нужно (представьте, что это шаблон для округления). Если ваши данные абсолютно должны быть с плавающей запятой, конвертируйте обратно в число с плавающей запятой только после того, как все ваши десятичные вычисления будут выполнены.

Наконец: я не уверен, что это охватывает все возможные странные угловые случаи в поведении с плавающей запятой в Python 2.4. Если вы полагаетесь на точное поведение Python 2.4, возможно, ничто не заменит работу на Python 2.4. То, что я описал выше, всего лишь на один шаг ближе к округлению в человеческом стиле.

person John Y    schedule 20.12.2011
comment
Поскольку ближайшее двоичное представление 1480.395 находится на волоске, округление до любого числа цифр 3 или больше просто оставляет вас там, где вы начали - двойное округление не помогает. - person Mark Ransom; 21.12.2011
comment
@Mark: Нет, я не предлагаю округлять время; Я говорю вокруг десятичной дроби (используя quantize). уточню в ответ... - person John Y; 21.12.2011

Вероятно, нет, так как Python 2.4 на самом деле вернул неверный результат. Поскольку вы округляете количество, которое строго меньше 5, правильный результат следует округлить в меньшую сторону.

Если вы сможете описать именно то поведение, которое вам нужно, возможно, мы сможем создать альтернативный подход.

person casevh    schedule 20.12.2011

В разделе что нового в Python 2.6 я вижу следующее (со ссылкой на PEP 3141):

Python 3.0 добавляет несколько абстрактных базовых классов для числовых типов, вдохновленных числовой башней Scheme. Эти классы были перенесены в 2.6 как модуль чисел.

...

В Python 3.0 PEP слегка переопределяет существующие встроенные функции round(), math.floor(), math.ceil() и добавляет новую, math.trunc(), которая была перенесена в Python 2.6. math.trunc() округляет до нуля, возвращая ближайший интеграл, который находится между аргументом функции и нулем.

И, благодаря комментарию @mark-dickinson, в разделе что нового в python 2.7 странице найден следующий текст:

Функция round() также теперь корректно округляется.

Так что да, изменение произошло в python 2.7. Это можно проверить, проверив, что в python 2.6 поведение все еще старое:

Python 2.6.7 (r267:88850, Aug 11 2011, 12:18:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f = 1480.39499999999998181010596454143524169921875
>>> round(f, 2)
1480.4000000000001

В моем дистрибутиве Linux у меня все еще есть доступ как к Python 2.6, так и к Python 2.7, и, возможно, у вас также есть доступ к вашему; так что, если вы хотите старое поведение, возможно, вы можете попробовать запустить код в python 2.6.

person jcollado    schedule 20.12.2011
comment
Соответствующее изменение было на самом деле в Python 2.7, а не в Python 2.6, хотя оно получило лишь краткое упоминание в документе «Что нового» версии 2.7: функция round() теперь также правильно округляется. - person Mark Dickinson; 22.12.2011
comment
@MarkDickinson Спасибо, я обновил ответ предоставленной вами информацией. - person jcollado; 22.12.2011

Возможно, я пропустил это в других ответах, но на самом деле функция round последней версии Python 2.# (версия 2.7.18) также не является «правильной», то есть не соответствует стандарту IEC 60559. Попробуй это:

print(round(2.25, 1))

И вы должны получить 2.2, а не 2.3. Это было исправлено в версиях 3.# (я тестировал только 3.5 и выше).

person David.    schedule 17.05.2021
comment
Что ж, в стандарте указано 5 различных режимов округления, не говоря уже о том, что один из них правильный, а другие неправильные. Python 2 использует режим до ближайшего, связывает от нуля. Это то, чему большинство из нас учили в школе. Python 3 использует ближайший, привязанный к четному режиму. Когда люди говорят, что округление было исправлено в Python 2.7, они имеют в виду, что ранее Python даже не всегда следовал своему предполагаемому режиму, как показывает пример OP. - person John Y; 08.07.2021
comment
Спасибо. Но теперь вы говорите так, как будто различные процедуры округления одинаково действительны, и что главная забота конечного пользователя состоит в том, чтобы Python следовал любой из этих процедур. Я бы сказал, что процедуры не являются одинаково действительными и что главная задача состоит в том, чтобы иметь наилучшую доступную процедуру. Рекомендуется округлять до четных, чтобы избежать систематической ошибки. Другими словами: чтобы не так часто ошибаться. - person David.; 10.07.2021
comment
Предположительно, стандарт не определял бы различные режимы округления, если бы каждый из них не служил какой-либо цели. Разработчики Python 2 и многих других языков специально решили реализовать округление наполовину от нуля, потому что они считали, что это лучше всего удовлетворяет принципу наименьшего удивления, особенно для встроенной (в отличие от математической или научной библиотечной функции) . Многих пользователей это беспокоит больше, чем избежание того, что они могут считать очень небольшой или даже незаметной предвзятостью. - person John Y; 14.07.2021