Как округлить число в Python до нужного количества знаков?
Нужно округлить дробное число до 2 знаков после запятой. Использую round(), но иногда получаю странности типа round(2.675, 2) даёт 2.67, а не 2.68. Почему так и как округлить правильно для денег?
2 ответа
Базово округляет функция round(число, знаки):
round(3.14159, 2) # 3.14
round(2.5) # 2 (внимание!)
round(123.456, 0) # 123.0
round(1234, -2) # 1200 — округление до сотен
Теперь про два сюрприза.
Сюрприз 1 — банковское округление. round округляет «к ближайшему чётному», поэтому round(2.5) это 2, а round(3.5) это 4. Это сделано, чтобы не накапливалась систематическая ошибка при суммировании.
Сюрприз 2 — round(2.675, 2) даёт 2.67. Это не баг round, а особенность дробных чисел: 2.675 в двоичной системе хранится как чуть-чуть меньше 2.675, и округляется вниз. Так работает плавающая точка во всех языках.
Для денег не используйте float и round — берите Decimal, который считает точно в десятичной системе:
from decimal import Decimal, ROUND_HALF_UP
price = Decimal('2.675')
print(price.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)) # 2.68
Обратите внимание — Decimal создают из строки '2.675', а не из float, иначе неточность float попадёт внутрь.
Если точность не критична (вывод на экран), часто проще округлить при форматировании: f'{x:.2f}' — это тоже округление до 2 знаков, но результат уже строка.
Если нужно именно «как в школе» — половинки всегда вверх — а тянуть Decimal не хочется, для положительных чисел сработает приём с математикой:
import math
def round_half_up(x, n=0):
factor = 10 ** n
return math.floor(x * factor + 0.5) / factor
Но помните: из-за всё той же плавающей точки идеальной точности это не даёт. Для финансов единственный надёжный путь — Decimal. Для отображения чисел пользователю обычного round или f'{x:.2f}' почти всегда достаточно.