Математическое округление чисел средствами Python

Раздел: Числа -> Математические операции

Округление чисел одна из базовых операций при обработке данных. В Python есть встроенная функция round(), а также дополнительные возможности в модулях math и decimal. Ниже рассмотрены основные способы округления и их особенности.

Основной способ округления

Функция round(number, ndigits) возвращает число, округлённое до ndigits знаков после десятичной точки. Если ndigits не указано, округление происходит до ближайшего целого.


x = 3.14159
rounded = round(x, 2)
print(rounded)
  

Python среднее число (вычисление среднего числа в python)

3.14

Abs x python (функция abs() в python)

Важная особенность: функция round использует банковское округление (round half to even). Если дробная часть ровно 0.5, число округляется до ближайшего чётного целого. Например, round(2.5) даёт 2, а round(3.5) даёт 4.


print(round(2.5))
print(round(3.5))
  

Python найти максимальное значение (поиск максимального значения в python)

2
4

числа словами python (преобразование чисел в слова в python)

Такое поведение уменьшает накопление ошибки при статистической обработке, но может быть неожиданным для новичков.

Как округлить число до целого?

Простейший способ - вызов round(x) без второго аргумента. Однако из-за банковского округления результат может отличаться от привычного математического округления. Для классического округления «от половины вверх» можно использовать трюк: int(x + 0.5) для положительных чисел.


x = 2.5
print(round(x))          # 2 (половина к чётному)
print(int(x + 0.5))      # 3 (классическое)
  

округление python round (округление чисел в python (round))

Для отрицательных чисел этот трюк не подходит - нужно учитывать знак.

Как округлить до заданного количества знаков после запятой?

Второй аргумент ndigits задаёт точность. Если ndigits отрицательное, округление происходит до десятков, сотен и т.д.


print(round(123.456, -1))  # 120.0
print(round(123.456, -2))  # 100.0
  

Для вывода с фиксированным числом знаков часто используют форматированные строки:


x = 3.14159
print(f"{x:.3f}")        # '3.142'
  

Форматирование тоже округляет, но использует правила, аналогичные round.

Как избежать банковского округления?

Если требуется математическое округление (0.5 всегда вверх), следует воспользоваться модулем decimal с контекстом округления ROUND_HALF_UP.


from decimal import Decimal, ROUND_HALF_UP

x = Decimal('2.5')
rounded = x.quantize(Decimal('1'), rounding=ROUND_HALF_UP)
print(rounded)  # 3
  

Использование строки '2.5' вместо числа с плавающей точкой гарантирует точное представление.

Как округлить число вверх или вниз?

Функции math.ceil() и math.floor() округляют число до ближайшего целого вверх или вниз соответственно. Для отрицательных чисел поведение может быть неочевидным: math.floor(-1.5) даёт -2, а math.ceil(-1.5) даёт -1.


import math
print(math.ceil(3.1))    # 4
print(math.floor(3.9))   # 3
print(math.ceil(-3.1))   # -3
print(math.floor(-3.1))  # -4
  

Как отбросить дробную часть?

Чтобы просто отсечь дробную часть, можно использовать int() или math.trunc(). Для положительных чисел это эквивалентно floor, для отрицательных - ceil.


print(int(3.7))          # 3
print(int(-3.7))         # -3
print(math.trunc(-3.7))  # -3
  

Типичные ошибки и проблемы

Проблема 1. Неожиданные результаты из-за представления чисел с плавающей точкой. Например, round(2.675, 2) даёт 2.67, а не 2.68. Это связано с тем, что 2.675 в двоичном представлении не может быть точно сохранён, и хранится как 2.6749999999999998.


print(round(2.675, 2))   # 2.67
  

Решение: использовать Decimal со строковым литералом.


from decimal import Decimal, ROUND_HALF_UP
x = Decimal('2.675')
print(x.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))  # 2.68
  

Проблема 2. Округление целых чисел с большим количеством знаков может привести к потере точности из-за преобразования в float при использовании round с большим ndigits. Рекомендуется работать с Decimal для финансовых расчётов.

Проблема 3. Путаница между round и форматированием строк: f"{x:.2f}" возвращает строку, а не число. Если требуется дальнейшая математическая обработка, лучше оставить числовой тип.

Ниже приведены более сложные сценарии использования округления в Python с подробными пояснениями.

Расширенные примеры

Округление до степеней десяти с отрицательным ndigits

Функция round принимает отрицательный ndigits, что позволяет округлять до десятков, сотен, тысяч и т.д. Это удобно для грубого округления больших чисел.

Пример

# Округление до ближайшего десятка
print(round(1234.56, -1))   # 1230.0
# Округление до ближайшей сотни
print(round(1234.56, -2))   # 1200.0
# Округление до ближайшей тысячи
print(round(1234.56, -3))   # 1000.0
1230.0
1200.0
1000.0

Банковское округление на границе 0.5

Поведение round для значений с дробной частью 0.5 демонстрируется ниже.

Пример

values = [0.5, 1.5, 2.5, 3.5, 4.5, -0.5, -1.5, -2.5]
for v in values:
    print(f"round({v}) = {round(v)}")
round(0.5) = 0
round(1.5) = 2
round(2.5) = 2
round(3.5) = 4
round(4.5) = 4
round(-0.5) = 0
round(-1.5) = -2
round(-2.5) = -2

Стоит отметить: 0.5 округляется до 0 (чётное число), 1.5 до 2 (чётное), -0.5 до 0 (чётное). Это и есть банковское округление.

Математическое округление с использованием пользовательской функции

Для получения округления «от половины вверх» для любого знака можно написать универсальную функцию.

Пример

import math

def round_half_up(x, ndigits=0):
    factor = 10 ** ndigits
    return math.floor(x * factor + 0.5) / factor

print(round_half_up(2.5))        # 3.0
print(round_half_up(-2.5))       # -2.0 (внимание: -2.5 -> -2, т.к. floor(-2.5*1+0.5) = floor(-2.0) = -2)
print(round_half_up(2.675, 2))   # 2.68 (но из-за float может быть 2.67)
3.0
-2.0
2.67

Последний пример показывает, что из-за неточности float даже пользовательская функция не всегда даёт ожидаемый результат. Поэтому для финансовых расчётов лучше использовать Decimal.

Округление с Decimal и настройкой точности

Модуль decimal позволяет контролировать точность и режим округления. Установим контекст с высокой точностью и округлением ROUND_HALF_UP.

Пример

from decimal import Decimal, ROUND_HALF_UP, getcontext

getcontext().prec = 10  # устанавливаем точность в 10 значащих цифр

x = Decimal('2.675')
rounded = x.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print(rounded)   # 2.68

# Также можно округлить до целого
y = Decimal('2.5')
print(y.quantize(Decimal('1'), rounding=ROUND_HALF_UP))   # 3
2.68
3

Разница между floor, ceil и trunc для отрицательных чисел

Важно различать эти функции, особенно при работе с отрицательными значениями.

Пример

import math
x = -3.7
print(f"math.floor({x}) = {math.floor(x)}")    # -4
print(f"math.ceil({x}) = {math.ceil(x)}")      # -3
print(f"math.trunc({x}) = {math.trunc(x)}")    # -3
print(f"int({x}) = {int(x)}")                  # -3
math.floor(-3.7) = -4
math.ceil(-3.7) = -3
math.trunc(-3.7) = -3
int(-3.7) = -3

Как видно, floor всегда идёт вниз (более отрицательное значение), а ceil вверх (к нулю для отрицательных). trunc и int просто отбрасывают дробную часть, двигаясь к нулю.

Форматирование строк как альтернатива округлению

Иногда требуется только отобразить округлённое значение, не меняя исходное число. Для этого подходят f-строки, метод format() или %-форматирование.

Пример

x = 3.1415926535
formatted = f"{x:.2f}"
print(formatted)             # '3.14'
print(type(formatted))       # 

# Сравнение с round
rounded = round(x, 2)
print(rounded)               # 3.14
print(type(rounded))         # 
3.14
<class 'str'>
3.14
<class 'float'>

Выбор зависит от задачи: для вычислений нужен float или Decimal, для отображения - строка.

Округление с динамическим количеством знаков

Количество знаков может быть переменной.

Пример

def round_dynamic(x, n):
    return round(x, n)

print(round_dynamic(3.14159, 3))   # 3.142
print(round_dynamic(3.14159, 1))   # 3.1
3.142
3.1

Округление чисел в Python (round) - comments

En
округление python round (python)