Почему round(2.675, 2) в Python выводит 2.67

Почему round(2.675, 2) в Python выводит 2.67

@python_quiz

Разберем этот квиз

Коротко: код

python

печатает 2.67. Это выглядит неожиданно, если думать в десятеричной арифметике, но логично для двоичного представления чисел с плавающей точкой. Разберёмся, почему так происходит и как получить «ожидаемое» поведение.

Что происходит в коде

  • Число 2.675 в памяти хранится не как точная десятеричная дробь 2.675, а как двоичная приближённая величина.
  • При преобразовании десятеричной дроби в двоичную получается значение чуть меньше 2.675 (примерно 2.6749999999999998).
  • Функция round смотрит на это фактическое значение и округляет «вниз» до 2.67.

Демонстрация (показываем внутреннее представление)

python

Одна из форм вывода ясно показывает, что float равен примерно 2.6749999999999998 — отсюда и поведение округления.

Решение, если нужно «идеальное» десятеричное округление

Для финансовых расчётов и там, где важна десятеричная точность, используйте модуль decimal:

python

Если хотите простое и надёжное десятеричное поведение — передавайте строковое представление в Decimal.

Анализ вариантов (коротко)

  • "2.67" — именно такой результат даёт приведённый код в обычном CPython благодаря внутреннему представлению float.
  • "2.68" — это ожидаемое десятеричное округление числа 2.675, но для float в памяти число хранится немного меньше, поэтому результат отличается.
  • "Ошибка округления" — ошибки как исключения здесь нет; это преднамеренное следствие представления чисел с плавающей точкой.
  • "Зависит от версии Python" — поведение обусловлено IEEE‑754 float и тем, как представляются двоичные приближения, а не версией языка; для практических целей результат одинаков в современных реализациях CPython.

Выводы и рекомендации

  • Округления вида round(float, n) зависят не только от логики округления, но и от представления числа в памяти.
  • Для точного десятеричного округления используйте decimal с передачей литерала в виде строки или храните значения в целых (копейки, центы) и округляйте целыми арифметическими операциями.
  • Для диагностики всегда полезно посмотреть полное представление float через format(x, '.17f') или Decimal.from_float(x), чтобы убедиться, какое фактическое значение хранится в переменной.

Report Page