TLDR;)
Проблема округления ввода / вывода была окончательно решена с помощью Python 2.7.0 и 3.1 .
.
Правильно округленное число может быть обратимо преобразовано назад и вперед:
str -> float() -> repr() -> float() ...
или Decimal -> float -> str -> Decimal
Десятичный тип больше не нужен для хранения.
(Естественно, может потребоваться округлить результат сложения или вычитания округленных чисел, чтобы исключить накопленные ошибки в последнем бите. Явная десятичная арифметика все еще может быть удобной, но преобразование в строку на str()
(то есть с округлением до 12 действительных цифр) обычно достаточно хорош, если не требуется предельная точность или экстремальное число последовательных арифметических операций.)
Бесконечный тест :
import random
from decimal import Decimal
for x in iter(random.random, None): # Verify FOREVER that rounding is fixed :-)
assert float(repr(x)) == x # Reversible repr() conversion.
assert float(Decimal(repr(x))) == x
assert len(repr(round(x, 10))) <= 12 # Smart decimal places in repr() after round.
if x >= 0.1: # Implicit rounding to 12 significant digits
assert str(x) == repr(round(x, 12)) # by str() is good enough for small errors.
y = 1000 * x # Decimal type is excessive for shopping
assert str(y) == repr(round(y, 12 - 3)) # in a supermaket with Python 2.7+ :-)
Документация
См. Примечания к выпуску Python 2.7 - Изменения в других языках четвертый абзац:
Преобразования между числами с плавающей точкой и строками теперь правильно округлены на большинстве платформ. Эти преобразования происходят во многих разных местах: str () для чисел с плавающей точкой и комплексных чисел; поплавок и сложные конструкторы; числовое форматирование; сериализация и десериализация чисел с плавающей точкой и комплексных чисел с использованием модулей marshal
, pickle
и json
; парсинг float и мнимых литералов в коде Python; и десятичное преобразование в число с плавающей точкой.
В связи с этим repr () числа с плавающей запятой x теперь возвращает результат, основанный на самой короткой десятичной строке , которая гарантированно округляется до x при правильном округлении (с режимом округления от половины до четности). Ранее он давал строку, основанную на округлении x до 17 десятичных цифр.
Смежный вопрос
Дополнительная информация: Форматирование float
до Python 2.7 было похоже на текущий numpy.float64
. Оба типа используют одинаковую 64-битную IEEE 754 двойную точность с 52-битной мантиссой. Большая разница в том, что np.float64.__repr__
часто форматируется с чрезмерным десятичным числом, так что ни один бит не может быть потерян, но между 13.949999999999999 и 13.950000000000001 не существует действительного номера IEEE 754. Результат не очень хороший, и преобразование repr(float(number_as_string))
не обратимо с помощью numpy. С другой стороны: float.__repr__
отформатирован так, что важна каждая цифра; последовательность без пробелов и конверсия обратима. Проще говоря: если у вас, возможно, есть номер numpy.float64, преобразуйте его в обычное число с плавающей точкой, чтобы его можно было отформатировать для людей, а не для числовых процессоров, в противном случае в Python 2.7 + больше ничего не нужно