Округление в Python: выбираем лучший способ
Отсекаем хвосты числовым змеям без потерь в точности.


Иллюстрация: Катя Павловская для Skillbox Media
Когда работаешь с десятичными дробями в коде, часто получаются монструозные числа с кучей знаков после запятой. В этой статье научимся сокращать значения так, чтобы не потерять в точности и не внести погрешности.
6 способов округления в Python
Функция round() в Python — стандартный способ
В Python есть встроенная функция для округления чисел. В общем виде она выглядит так: round(number, digits). В качестве первого аргумента (number) передаём дробное число, а во втором (digits) указываем количество чисел после запятой. Второй аргумент по умолчанию равен нулю.
round(6.4457, 2) # 6,45
Важно отметить, что алгоритм вычисления функции round() различается в разных версиях языка. В Python 2 за основу взяты классические правила арифметики. То есть 1, 2, 3, 4 после запятой ведут к округлению в меньшую сторону, а 5, 6, 7, 8, и 9 — в большую.
Например:
- 7,6 → 8 — округление в большую сторону.
- 7,4 → 7 — округление в меньшую сторону.
В Python 3 используется так называемый «банковский» метод, то есть округление до ближайшего чётного числа:
- 3,5 → 4.
- 6,5 → 6.
В этой статье мы будем использовать Python 3. Вот как работает округление в этой версии с помощью функции round():
round(7) # 7
round(7.5) # 8
round(7.56986, 2) # 7.57
Видно, что round() привела число к ближайшему чётному, и число 7,5 округлилось до 8. В последнем случае мы задали точность вычислений и оставили только два знака после запятой. С отрицательными числами всё работает так же:
round(-7) # -7
round(-7.5) # -8
round(-7.56986, 2) # -7.57
Чтобы получить целое значение, функция round() приводит результат к типу int. Именно поэтому 7,7 превращается в 8, а не в 8,0.
Функция int() в Python — быстрый и грубый способ
Есть и более быстрый способ округлить число в Python — полностью отбросить дробную часть. Чтобы это сделать, надо привести дробное число к целому с помощью встроенной функции int(). Такой способ работает безотказно и не требует импорта дополнительных модулей. Посмотрим на примеры:
int(7.1) # 7
int(7.9) # 7
int(7.899) # 7
Во всех примерах выше мы получаем число 7 после округления. Теперь провернём то же самое, но для отрицательных значений:
int(-7.1) # -7
int(-7.9) # -7
int(-7.899) # -7
Ничего не поменялось, и Python всё равно отбрасывает всё, что находится после запятой. Это очень грубый подход, который противоречит правилам арифметического округления. Но можно добавить точности с помощью простого условия: прибавим к числу 0,5, если оно больше нуля, а если меньше — −0,5:
def correct_rounding(number):
if number > 0:
return int(number + 0.5)
else:
return int(number + -0.5)
correct_rounding(7.1) # 7
correct_rounding(7.7) # 8
correct_rounding(-7.1) # -7
correct_rounding(-7.7) # -8
В этом случае округление будет работать правильно, но нам пришлось написать дополнительную функцию. В Python есть более быстрые и удобные способы.
Модуль math в Python — когда важна точность
Если хотите точности без написания дополнительных алгоритмов, можно подключить встроенную в Python библиотеку math. В ней есть функции, которые позволяют округлить число быстро и без программных костылей.
Вот эти функции:
- ceil() — округление в большую сторону;
- floor() — округление в меньшую сторону;
- trunc() — отбрасывание дробной части.
Дальше мы расскажем о каждой функции подробнее. Но сначала — импортируем модуль math в наш проект. Для этого пропишем команду import math в начале файла. Если пропустить этот шаг, Python просто не поймёт, как ему исполнять эти функции.
math.ceil
Функция ceil() округляет результат в большую сторону. Например, число 25,5 находится между 25 и 26, поэтому алгоритм автоматически округлит его до 26:
import math
math.ceil(25.1) # 26
math.ceil(25.5) # 26
math.ceil(25.9) # 26
Этот же принцип работает и с отрицательными числами:
import math
math.ceil(-25.1) # -25
math.ceil(-25.5) # -25
math.ceil(-25.9) # -25
В Python 3 функция math.ceil возвращает целое число (int), а в Python 2 — число с плавающей точкой (float).
math.floor
Для округления в меньшую сторону используют функцию floor(). Принцип её работы идентичен ceil(), только при сравнении Python пойдёт вниз:
import math
math.floor(25.1) # 25
math.floor(25.5) # 25
math.floor(25.9) # 25
Всё то же самое происходит и с отрицательными числами. Python смотрит на соседние целые числа, выбирает, какое из них ближе и округляет до него. В случае с −25 меньшим числом будет −26:
import math
math.floor(-25.1) # -26
math.floor(-25.5) # -26
math.floor(-25.9) # -26
math.trunc
Эта функция из математического модуля Python просто отбрасывает все числа после запятой, оставляя только целую часть:
import math
math.trunc(25.1)
math.trunc(25.5)
math.trunc(25.9)
Это очень похоже на округление с помощью int(). Поэтому, если вам нужен быстрый и грубый способ избавиться от дроби, лучше пользоваться именно ей. Не стоит подключать в проект целый модуль ради одной функции, если, конечно, вы не любитель палить из пушки по воробьям :)
Модуль decimal в Python — ещё больше точности
Python не всегда точно обрабатывает дробные числа. В этом можно убедиться на простом примере:
print(0.1 * 3) # 0.30000000000000004
Должно получиться 0,3, но вместо этого появилось много нулей после запятой. Так получается из-за перевода десятичных дробей в двоичную систему в памяти компьютера. Эта же особенность иногда приводит к ошибкам при округлении:
round(2.65, 1) # 2.6
round(2.85, 1) # 2.9
В первом случае Python округлил в меньшую сторону, а во втором — в большую, хотя в обеих дробях стоит одна и та же пятёрка на конце. Если хотите получать более предсказуемый результат, можно использовать модуль decimal.
Для начала импортируем модуль командой from decimal import *. После этого создадим объект числа с плавающей точкой с помощью класса Decimal.
Ошибок при вычислении уже не будет:
from decimal import *
number = Decimal("0.1")
number = number * 3 # 0.3
Для округления в Decimal есть метод quantize(). Но, чтобы он нормально работал, ему нужно показать, как именно форматировать число. Для этого передадим ему в качестве аргумента ещё один объект Decimal с числом-шаблоном:
from decimal import *
number = Decimal("3.5648575")
number = number.quantize(Decimal("1.000")) # 3.565
Видим, что число 1.000 показывает алгоритму, сколько именно знаков нужно оставить после запятой.
Теперь вернёмся к первому примеру, где метод round() округлял похожие числа по-разному. С помощью Decimal, получим более прогнозируемый результат:
from decimal import *
number = Decimal("2.65") # Округляем сотые до десятых
number = number.quantize(Decimal("1.0")) # 2.6
number = Decimal("2.85")
number = number.quantize(Decimal("1.0")) # 2.8
Если в программе важна высокая точность при округлении чисел, то лучше использовать модуль decimal. Код получится немного сложнее, но это убережёт от получения непредсказуемых результатов и ошибок в подсчётах.
Что в итоге
- Самый простой способ округления — отбросить дробную часть с помощью метода int(). Однако этот подход довольно грубый и не подходит для решения задач, в которых важна высокая точность.
- Для округления в Python предусмотрена нативная функция round(), но алгоритм вычислений различается в зависимости от версии языка программирования. С этим надо быть внимательнее.
- В модуле math содержатся уже готовые функции ceil(), floor() и trunc(). С ними проще работать, а результат получается более предсказуемым.
- Из-за особенностей компьютерных вычислений Python не всегда точно обрабатывает десятичные дроби. Чтобы избежать связанных с этим ошибок, используйте модуль decimal.
Больше интересного про код — в нашем телеграм-канале. Подписывайтесь!