Код
#статьи

Округление в 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.

Больше интересного про код — в нашем телеграм-канале. Подписывайтесь!

Нейросети для работы и творчества!
Хотите разобраться, как их использовать? Смотрите конференцию: четыре топ-эксперта, кейсы и практика. Онлайн, бесплатно. Кликните для подробностей.
Смотреть программу
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована