Decimal в Python: как считать суммы и явно выбирать округление

Decimal в Python: как считать суммы и явно выбирать округление
Содержание статьи

Для суммы, которую нужно хранить и округлять по заданному правилу, используйте Decimal из строк и явно задавайте режим округления. Преобразование уже полученного float не восстанавливает исходную десятичную запись. На Mentoring.digital разберём небольшой расчёт скидки и проверки, которые помогут заметить неоднозначность требований.

Сначала определите правило расчёта

До кода согласуйте три вещи: допустимое число знаков на входе, момент округления и способ обработки половины. Это разные решения. Если округлять каждую строку заказа и если сначала сложить точные значения, итог в общем случае может отличаться. Выбор не должен случайно зависеть от места вызова функции.

Decimal подходит для десятичной арифметики, но сам по себе не определяет бизнес-правило. В примере ниже выбран ROUND_HALF_UP. Это условие примера, а не универсальное требование для всех платежей, валют и документов.

Расчёт без промежуточного float

from decimal import Decimal, ROUND_HALF_UP

CENT = Decimal("0.01")

def discounted_total(price: str, quantity: int, rate: str) -> Decimal:
    amount = Decimal(price) * quantity
    result = amount * (Decimal("1") - Decimal(rate))
    return result.quantize(CENT, rounding=ROUND_HALF_UP)

assert discounted_total("19.95", 3, "0.03") == Decimal("58.05")
assert Decimal("2.345").quantize(CENT, rounding=ROUND_HALF_UP) == Decimal("2.35")
assert Decimal("-2.345").quantize(CENT, rounding=ROUND_HALF_UP) == Decimal("-2.35")

Здесь цена и доля скидки поступают строками. Скидка 3% представлена как 0.03. Количество целое, а округление выполняется один раз для окончательного результата. Проверка отрицательного значения показывает выбранное правило на возврате, но не заменяет правила оформления возврата в конкретной системе.

Что пример намеренно не делает

Функция не валидирует пользовательский ввод. В приложении отдельно проверьте допустимый диапазон скидки, количество, валюту, конечность значений и правила отрицательных сумм. Строки NaN и Infinity нельзя молча принимать как обычные денежные значения. Ошибка разбора должна становиться понятным отказом, а не нулевой суммой.

Контекст Decimal ограничивает точность операций. Для очень больших чисел или сложных цепочек нужен отдельный выбор точности и тест границ. Форматирование строки для экрана также не следует путать с арифметическим округлением хранимого результата.

Минимальная проверка перед использованием

  • Проверьте целую сумму, половину копейки и отрицательную половину.
  • Сравните округление каждой позиции и округление общей суммы на специально подобранном примере.
  • Проверьте некорректную строку, слишком большую сумму и недопустимую скидку.
  • Убедитесь, что между API, базой и расчётом не появился float.

Пример можно выполнить как обычный Python-файл. Для проектирования обработки ввода и проверок полезен разбор с наставником по Python. Критерий готовности: ожидаемое округление записано в тесте и согласовано с назначением расчёта.

Похожие статьи по данной теме