Подтвердить что ты не робот

Проблемы с округлением десятичных разрядов (python)

В моей программе десятичная точность очень важна.
Многие мои вычисления должны быть точными для многих знаков после запятой (например, 50).

Поскольку я использую python, я использую десятичный модуль  (с контекстом(). prec = 99. ie. Установите, чтобы иметь 99 знаков после запятой при создании экземпляра десятичного объекта)
поскольку питонические поплавки не позволяют приблизить такую ​​точность.

Поскольку я хочу, чтобы Пользователь указывал десятичные точки точности вычислений, мне пришлось реализовать несколько функций round() в моем коде.
К несчастью, встроенная функция раунда и десятичный объект плохо взаимодействуют.

round(decimal.Decimal('2.000000000000000000001'),50)

# Number is 1e-21.   There are 21 decimal places, much less than 50.

Однако результат 2.0 вместо 2.000000000000000000001
Круговая функция не округляется до 50. Гораздо меньше!

Да, я убедился, что переопределения не происходит при создании объекта Decimal, но после вызова раунда.
Я всегда передаю строки, представляющие поплавки, в конструктор Decimal, никогда не pythonic плавает.

Почему круглая функция делает это со мной?
(Я понимаю, что он, вероятно, был первоначально разработан для pythonic float, который никогда не может иметь столько десятичных знаков, но в документации утверждается, что объект Decimal идеально интегрируется в код python и совместим со встроенными функциями python!)

Спасибо, спасибо!
(Это меня раздражает, так как эта проблема подрывает использование всей программы)

Технические характеристики:
python 2.7.1
Windows 7
десятичный модуль (встроенный)

4b9b3361

Ответ 1

Проблема, как только я могу ее определить, заключается в том, что round() возвращает тип Python float, а не тип Decimal. Таким образом, не имеет значения, какую точность вы устанавливаете в модуле Decimal, потому что после вызова round() у вас больше нет Decimal.

Чтобы обойти это, вам, вероятно, придется придумать альтернативный способ округления ваших чисел, который не зависит от round(). Например, предложение Раймонда.

Вы можете найти этот пример короткого идеона: http://ideone.com/xgPL9

Ответ 2

Так как round() принудительно вводит свой вход в обычный двоичный float, предпочтительный способ округления десятичных объектов - с метод quantize():

>>> from decimal import Decimal
>>> d = Decimal('2.000000000000000000001')

>>> d.quantize(Decimal(10) ** -20)     # Round to twenty decimal places
Decimal('2.00000000000000000000')

Чтобы отменить конечные нули, примените normalize() к округленному результату:

>>> d = Decimal('2.000000000000000000001')
>>> d.quantize(Decimal(10) ** -20).normalize()
Decimal('2')

Ответ 3

Попробуйте следующее...

from decimal import Decimal, ROUND_HALF_UP, getcontext

getcontext().prec = 51

def round_as_decimal(num, decimal_places=2):
    """Round a number to a given precision and return as a Decimal

    Arguments:
    :param num: number
    :type num: int, float, decimal, or str
    :returns: Rounded Decimal
    :rtype: decimal.Decimal
    """
    precision = '1.{places}'.format(places='0' * decimal_places)
    return Decimal(str(num)).quantize(Decimal(precision), rounding=ROUND_HALF_UP)

round_as_decimal('2.000000000000000000001', decimal_places=50)

Надеюсь, что это поможет!

Ответ 4

Использование round() для любых целей имеет трудности:

(1) В Python 2.7 раунд (Decimal ('.39.142')) создает 39.14, то есть float; 3.2 выдает десятичное число.

>>> import decimal; round(decimal.Decimal('2.000000000000000000001'),50)
2.0   <<<=== ***float***

(2) round() имеет только один режим округления; Decimal.quantize имеет много.

(3) Для всех типов ввода изменился режим одиночного округления: Python 2.7 округляется от 0 (ожидается в большинстве бизнес-приложений), тогда как Python 3.2 раундов до ближайшего, даже кратного 10 ** - n (менее ожидаемый в бизнесе).

Используйте Decimal.quantize()

Ответ 5

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

    def dround(decimal_number, decimal_places):
        return decimal_number.quantize(Decimal(10) ** -decimal_places)