Округление в python

7 апреля 2016 г. 20:05

Округление в python осуществляется функцией round(number, ndigits), где number - округляемое число, а ndigits - количество знаков после запятой. Например:

round(2.137, 2)  # = 2.14

Иногда округление может дать неожиданный результат, например:

round(2.45, 2) # = 2.5
round(2.55, 2) # = 2.5, а не 2.6!

Это связано с неточностью представления типа float (см. Арифметика с плавающей точкой ). Так как в компьютере числа записываются в двоичном виде, то приблизительно можно посмотреть, как хранятся числа 2.45 и 2.55, с помощью строкового представления чисел в python. Выведем данные числа с точностью до 30 знаков после запятой:

>>> '%0.30f' % 2.45
'2.450000000000000177635683940025'
>>> '%0.30f' % 2.55
'2.549999999999999822364316059975'

Как видите, округляя значение 2.549999999999999822364316059975 до 1 знака после запятой, по правилам арифметического округления получим 2.5

Замечание
Имейте ввиду, что в python 3 (в отличии от python 2) используется банковское округление (что это см. ниже в разделе "Банковское округление в round()").

В python округление до верха (в большую сторону) math.ceil(x), а округление до низа (в меньшую сторону) math.floor(x), например:

import math

math.ceil(2.3)  # = 3.0 - округление до верха
math.floor(2.7)  # = 2.0 - округление до низа

Чтобы получить целое число, нужно просто привести полученный результат в int:

round(2.6)  # = 3.0
int(round(2.6))  # = 3

Чтобы просто отбросить знаки после запятой, можно сделать так:

int(2.6)  # = 2 , что эквивалентно int(math.floor(2.6))

Банковское округление в round()

Очень много идёт дискуссий ( pythonworld , stackoverflow форум linux.org.ru  и др.) на тему, какое же всё-таки округление в python арифметическое или банковское?

Оказывается в python 2 используется арифметическое округление, а в python 3 - банковское! Спасибо Дмитрию, внёсшему ясность в комментариях, по какому правилу идёт округление в разных python, благодаря подробному примеру и указанию на авторитетный источник.

Само банковское округление очень полезно для расчётов. Банковское (или бухгалтерское) округление позволяет уменьшить погрешности при работе с большим массивом данных. Обычное (или арифметическое) округление даёт нарастающую погрешность из-за того, что для округления в меньшую сторону на конце должны быть цифры: 1, 2, 3, 4 - всего 4 цифр, а в большую: 5, 6, 7, 8, 9 - всего 5 цифр. Неравное количество цифр и вызывают нарастающую погрешность в вычислениях. А банковское округление работает по статистическим законам: вероятность того, что перед пятёркой окажется чётное или нечётное число для большинства случаев (к примеру, бухгалтерские расчёты) примерно одинаковая, поэтому округление происходит к ближайшему чётному, то есть 2,5 → 2, 3,5 → 4. - такой принцип уменьшает погрешность.

Здесь вы можете увидеть подробности округления в официальных источниках документации python:

Обратите внимание, что документации разных версий python отличаются, а точнее в python 3 добавлено:

For the built-in types supporting round(), values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2).

"if two multiples are equally close, rounding is done toward the even choice" (перев.: Округление делается до ближайшего четного) - это и есть банковское округление.

 

Но напоследок приведу пример, наглядно демонстрирующий разницу работы функции round() в различных версиях python и с учётом неточности представления типа float:

Пример round() в python 2 round() в python 3 банковское округление
round(2.05, 1) 2.0 2.0 2.0
round(2.15, 1) 2.1 2.1 2.2
round(2.25, 1) 2.3 2.2 2.2
round(2.35, 1) 2.4 2.4 2.4
round(2.45, 1) 2.5 2.5 2.4
round(2.55, 1) 2.5 2.5 2.6
round(2.65, 1) 2.6 2.6 2.6
round(2.75, 1) 2.8 2.8 2.8
round(2.85, 1) 2.9 2.9 2.8
round(2.95, 1) 3.0 3.0 3.0

Жирным выделил отличающиеся значения выполнения функции round().

Казалось бы в примере с round(2.15, 1) результат для python 3 должен быть 2.2, потому как python 3 работает по банковскому округлению, но правильное значение 2.1, так как значение 2.1 в машинном представлении не точное и выглядит для, к примеру, 60 знаков после запятой следующим образом:

>>> '%0.60f' % 2.15
'2.149999999999999911182158029987476766109466552734375000000000'

Отсюда видно, что по правилам обычного арифметического округления получаем 2.1.

Но для примера round(2.25, 1) 60 знаков после запятой выглядят следующим образом:

>>> '%0.60f' % 2.25
'2.250000000000000000000000000000000000000000000000000000000000'

Вот здесь-то и возникает спорный момент по способу округления. Так для python 2 получаем 2.3 - арифметическое округление.

А для python 3 получаем 2.2, что соответствует правилам банковского округления.

Материалы по теме:

Оцените статью

5 из 5 (всего 10 оценок)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

Автор статьи

Права на использование материала, расположенного на этой странице http://vivazzi.ru/it/round-python/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="http://vivazzi.ru/it/round-python/">http://vivazzi.ru/it/round-python/</a>

Подробнее: Правила использования сайта

Комментариев: 6

Гость
Гость

26.02.2018 4:14 #

Заблуждение ! В python обычное математическое округление. Описанное поведение связано с неточностью представления десятичных чисел во float .

>>> '%0.30f' % 2.45
'2.450000000000000177635683940025'
>>> '%0.30f' % 2.55
'2.549999999999999822364316059975'

Ответить

Артём Мальцев
Артём Мальцев автор

26.02.2018 23:17 #

Гость, спасибо за ваш комментарий ! Миф о банковском округлении разрушен, статью подправил.

Ответить

Дмитрий
Дмитрий

09.06.2018 17:16 #

В python3 именно банковское округление. Но и неточности float тоже присутствуют.
Если Вы считаете что в python3 арифметическое округление, то объясните пожалуйста это:

>>> round(2.5)
2
>>> '%0.100f' % 2.5
'2.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
>>>

Ответить

Артём Мальцев
Артём Мальцев автор

10.06.2018 0:01 #

Да, согласен в python 2 и python 3 в примере round(2.5) дают разные результаты (2 и 3 соответственно) .

Хорошо, Дмитрий, приведите, пожалуйста, официальные источники, как всё-таки идёт округление в python. Пока из авторитетных источников я нашёл только это https://docs.python.org/2/library/functions.html#round (для python 2) и https://docs.python.org/3/library/functions.html#round (для python 3) - и в них не говорится о банковском округлении.

Ответить

Дмитрий
Дмитрий

11.06.2018 2:56 #

Собственно в py3 документации по Вашей ссылке об этом и сказано :

if two multiples are equally close, rounding is done toward the even choic e Округление делается до ближайшего четного - это и есть банковское округление . P.S. Чтобы выяснить, какое округление используется, нужно брать числа, которые не имеют неточностей и стоят ровно между двух чисел. Тогда начинает работать правило: либо это арифметическое (py2) - округляем вверх, либо это банковское (py3) - округляем до ближайшего четного. А если число не стоит ровно между двух чисел (например имеет неточности float), то и в py2 и в py3 работает простое правило - округляем до ближайшего числа.

Ответить

Артём Мальцев
Артём Мальцев автор

11.06.2018 7:26 #

Дмитрий, большое спасибо за подробное объяснение, как всё-таки идёт округление в python!
Моей ошибкой было не протестировать вручную, как происходит округление для python 3 - возможно, я лучше бы разобрался с этой темой.

Ответить

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

Чтобы оставить комментарий от своего имени войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте