Получить точное представление десятичной строки Python Decimal? - PullRequest
0 голосов
/ 03 января 2019

Если у меня есть Python Decimal, как я могу надежно получить точное представление десятичной строки (т. Е. Не научную запись) числа без конечных нулей?

Например, если у меня есть:

>>> d = Decimal('1e-14')

Мне бы хотелось:

>>> get_decimal_string(d)
'0.00000000000001'

Однако:

  1. В классе Decimal нет метода to_decimal_string, илидаже любой to_radix_string(radix) (ср .: https://docs.python.org/3/library/decimal.html#decimal.Context.to_eng_string)
  2. Форматер %f по умолчанию округляет до 6 десятичных знаков - '%f' %(d, ) ==> '0.000000' - или требует точного количества десятичных знаков.
  3. {:f}.format(...) форматер выглядит для работы - '{:f}'.format(d) ==> '0.00000000000001' - однако Я не хочу верить этому, так как это на самом деле противоречит документации , котораяговорит "'f' ... Отображает число в виде числа с фиксированной точкой. Точность по умолчанию составляет 6"
  4. Decimal.__repr__ и Decimal.__str__ иногда возвращают научную запись: repr(d) ==> "Decimal('1E-14')"

Итак, есть ли способ получить десятичную строку из Python Decimal? Или мне нужно свернуть свою собственную, используя Decimal.as_tuple()?

1 Ответ

0 голосов
/ 03 января 2019

Короткий ответ:

>>> d
Decimal('1E-14')
>>> '{:f}'.format(d)
'0.00000000000001'

Длинный ответ:

Как указал @BrandonRhodes PEP 3101 (то есть PEP строкового формата):

Синтаксис для спецификаторов формата является открытым, поскольку класс может переопределять стандартные спецификаторы формата.В таких случаях метод str.format () просто передает все символы между первым двоеточием и соответствующей скобкой в ​​соответствующий базовый метод форматирования.

И, таким образом, метод Decimal.__format__какой формат строки Python будет использовать для генерации str представления значения Decimal.В основном Decimal переопределяет форматирование, чтобы оно было «умным», но по умолчанию будет использовать любые значения, которые задает строка формата (т. Е. {:.4f} урежет десятичное число до 4 разрядов).

Вот почему вы можете доверять ему (фрагментfrom decimal.py:Decimal.__format__):

# PEP 3101 support.  the _localeconv keyword argument should be
# considered private: it's provided for ease of testing only.
def __format__(self, specifier, context=None, _localeconv=None):
    #
    # ...implementation snipped.
    #

    # figure out placement of the decimal point
    leftdigits = self._exp + len(self._int)
    if spec['type'] in 'eE':
        if not self and precision is not None:
            dotplace = 1 - precision
        else:
            dotplace = 1
    elif spec['type'] in 'fF%':
        dotplace = leftdigits
    elif spec['type'] in 'gG':
        if self._exp <= 0 and leftdigits > -6:
            dotplace = leftdigits
        else:
            dotplace = 1

    # find digits before and after decimal point, and get exponent
    if dotplace < 0:
        intpart = '0'
        fracpart = '0'*(-dotplace) + self._int
    elif dotplace > len(self._int):
        intpart = self._int + '0'*(dotplace-len(self._int))
        fracpart = ''
    else:
        intpart = self._int[:dotplace] or '0'
        fracpart = self._int[dotplace:]
    exp = leftdigits-dotplace

    # done with the decimal-specific stuff;  hand over the rest
    # of the formatting to the _format_number function
    return _format_number(self._sign, intpart, fracpart, exp, spec)

Короче говоря, метод Decimal.__format__ вычислит необходимое заполнение для представления числа до и после десятичной точки на основе возведения в степень, полученного из Decimal._exp (в вашем примере, 14 значащих цифр).

>>> d._exp
-14
...