В таблице стоит 2.675. После перевода во float и округления выходит 2.67. Если затем обернуть уже созданный float в Decimal, исходная десятичная запись сама не вернётся. Разберём два пути обработки одной учебной строки: через двоичное число и сразу из текста. Нужно понять, какое значение округляет программа, и сохранить источник числа до выбора правила отображения.
Все три исходные строки ниже придуманы для показа. Это не измерения реального исследования и не финансовый расчёт. Код выполнен в Python 3.12.14 без внешних пакетов. Сравниваются конкретные заданные числа и режим ROUND_HALF_UP; результат нельзя переносить на любую величину, точность или методику отчёта.
Два пути к Decimal начинаются с разных значений
В руководстве decimal показано создание десятичных чисел из разных типов. Decimal от float точно передаёт уже имеющееся двоичное приближение. Decimal от исходной строки получает её десятичное значение. Во втором случае не нужно сначала вызывать float: иначе часть информации может быть потеряна раньше округления.
Это не означает, что float всегда непригоден или что Decimal делает любую арифметику бесконечно точной. Для вычислений с Decimal действует контекст с конечной точностью. В нашем небольшом примере точности 28 значащих цифр достаточно для показанного quantize; число 28 не означает 28 знаков после запятой в отчёте. Причины двоичного представления объяснены в официальном разделе о float.
| Исходная строка | После округления до сотых: из float / из текста |
|---|---|
| 2.675 | 2.67 / 2.68 |
| 2.665 | 2.67 / 2.67 |
| -2.675 | -2.67 / -2.68 |

В двух строках из трёх пути дают разные результаты. Для -2.675 режим ROUND_HALF_UP даёт -2.68 на точном десятичном входе: на середине между ближайшими вариантами он выбирает направление от нуля. Не называйте это универсальным «увеличением числа» — для отрицательного значения числовое значение уменьшается. Правила режимов перечислены в документации округления.
Законченный пример: сохраните текст до преобразования
В выводе from_float означает «получено через float», from_text означает «из исходной строки», а float_exact показывает точное десятичное представление уже созданного float для первой строки. Длинная последняя запись позволяет увидеть, почему это значение находится не точно на десятичной середине 2.675.
Ниже полный код учебного примера и вывод, который был получен на самом деле. Обозначения в выводе объяснены рядом с примером.
from decimal import Decimal, ROUND_HALF_UP, localcontext
texts = ["2.675", "2.665", "-2.675"]
results = []
with localcontext() as ctx:
ctx.prec = 28
for text in texts:
from_float = Decimal(float(text)).quantize(
Decimal("0.01"), rounding=ROUND_HALF_UP)
from_text = Decimal(text).quantize(
Decimal("0.01"), rounding=ROUND_HALF_UP)
results.append((text, str(from_float), str(from_text)))
print(f"{text}: from_float={from_float}; from_text={from_text}")
print("float_exact =", Decimal(float(texts[0])))
assert results == [("2.675", "2.67", "2.68"),
("2.665", "2.67", "2.67"),
("-2.675", "-2.67", "-2.68")]
2.675: from_float=2.67; from_text=2.68
2.665: from_float=2.67; from_text=2.67
-2.675: from_float=-2.67; from_text=-2.68
float_exact = 2.67499999999999982236431605997495353221893310546875
Список results целиком сравнивается с ожидаемыми тремя строками с помощью assert. Эти проверки важно не удалять, иначе ошибочно изменённый пример будет выглядеть подтверждённым. Сначала задаётся квант 0.01, затем режим указывается явно. Это правило нашего учебного показа, а не автоматически подходящая точность для любого исследования.
Как применить приём к своей выгрузке
- Сохраните исходный файл и текстовую запись числа. Если данные уже поступили как float, отметьте это ограничение: преобразование в Decimal не доказывает восстановление исходной записи.
- Зафиксируйте правило округления и нужный десятичный разряд отдельно от точности промежуточных вычислений. Правило должно соответствовать задаче и требованиям отчёта.
- Разделяйте рабочие значения и отображение в таблице. Не округляйте каждое промежуточное действие без необходимости.
- Сверьте несколько пограничных положительных и отрицательных значений на собственном диапазоне; не считайте три примера универсальным валидатором.
Преобразование Decimal(str(x)) иногда даёт удобное десятичное представление отображаемого float x, но это уже текст от float, а не обязательно первоначальная запись из файла. Если исходные цифры важны, храните их до первого числового преобразования. В примере специально используются заранее заданные корректные конечные строки; обработку пропусков, NaN, бесконечностей и произвольного формата ввода он не реализует.
Что проверить в описании результата
| Проверка | Что записать для воспроизводимости |
|---|---|
| Источник числа | Исходный текст или уже готовый float; имя и версия файла |
| Промежуточная арифметика | Типы значений и точность контекста |
| Отображение | Квант 0.01 и явно выбранный ROUND_HALF_UP для этого примера |
| Ограничения | Конечный диапазон учебных строк; не общий парсер и не гарантия точности любого расчёта |
Если задача касается суммы показанных процентов, используйте отдельный разбор о том, почему округлённые доли могут дать 100,1%. Там рассматривается другой вопрос — итог таблицы, а здесь источник значения до округления. Правила импорта исходной записи связаны и с подготовкой CSV.
Когда после проверки формата нужно обсудить методику расчётов по вашему набору данных, на странице статистического анализа можно уточнить состав помощи. Сохраните исходную выгрузку и отдельно сформулируйте исследовательский вопрос. Исправленный способ представления числа сам по себе не подтверждает качество данных или научный вывод.