¿Obtener la representación exacta de la cadena decimal de Python Decimal?

Si tengo un Decimal Python, ¿cómo puedo obtener de manera confiable la representación exacta de la cadena decimal (es decir, no de la notación científica) sin ceros al final?

Por ejemplo, si tengo:

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

Me gustaría:

 >>> get_decimal_string(d) '0.00000000000001' 

Sin embargo:

  1. La clase Decimal no tiene ningún método to_decimal_string , ni siquiera to_radix_string(radix) (cf: https://docs.python.org/3/library/decimal.html#decimal.Context.to_eng_string )
  2. El formateador %f por defecto redondea a 6 lugares decimales – '%f' %(d, ) ==> '0.000000' – o requiere un número preciso de lugares decimales.
  3. El formateador {:f}.format(...) parece funcionar – '{:f}'.format(d) ==> '0.00000000000001'sin embargo , soy reacio a confiar en eso, ya que esto realmente va en contra de la documentación , que dice ” 'f' … Muestra el número como un número de punto fijo. La precisión predeterminada es 6″
  4. Decimal.__repr__ y Decimal.__str__ devuelven a veces notación científica: repr(d) ==> "Decimal('1E-14')"

Entonces, ¿hay alguna manera de obtener una cadena decimal de un decimal de Python? ¿O necesito rodar mi propio uso de Decimal.as_tuple() ?

Respuesta corta:

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

Respuesta larga:

Como @BrandonRhodes señaló que PEP 3101 (que es el formato de cadena PEP) dice:

La syntax para los especificadores de formato es abierta, ya que una clase puede anular los especificadores de formato estándar. En tales casos, el método str.format () simplemente pasa todos los caracteres entre los dos puntos y la llave correspondiente al método de formato subyacente relevante.

Y, por lo tanto, el método Decimal.__format__ es lo que el formato de cadena de python utilizará para generar la representación de la str del valor Decimal . Básicamente, Decimal anula el formato para que sea “inteligente”, pero se establecerá de manera predeterminada en cualquier valor que la cadena de formato establezca (es decir, {:.4f} truncará el decimal a 4 lugares).

Aquí es por qué puedes confiar en él (fragmento de 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) 

En pocas palabras, el método Decimal.__format__ calculará el relleno necesario para representar el número antes y después del decimal basado en la exponencia proporcionada por Decimal._exp (en su ejemplo, 14 dígitos significativos).

 >>> d._exp -14