If I've got a Python Decimal
, how can I reliably get the precise decimal string (ie, not scientific notation) representation of the number without trailing zeros?
For example, if I have:
>>> d = Decimal('1e-14')
I would like:
>>> get_decimal_string(d)
'0.00000000000001'
However:
Decimal
class doesn't have any to_decimal_string
method, or even any to_radix_string(radix)
(cf: https://docs.python.org/3/library/decimal.html#decimal.Context.to_eng_string)%f
formatter either defaults to rounding to 6 decimal places - '%f' %(d, ) ==> '0.000000'
- or requires a precise number of decimal places.{:f}.format(...)
formatter appears to work - '{:f}'.format(d)
==> '0.00000000000001'
- however I'm reluctant to trust that, as this actually runs counter to the documentation, which says "'f'
… Displays the number as a fixed-point number. The default precision is 6"Decimal.__repr__
and Decimal.__str__
sometimes return scientific notation: repr(d) ==> "Decimal('1E-14')"
So, is there any way to get a decimal string from a Python Decimal
? Or do I need to roll my own using Decimal.as_tuple()
?
modf() function is an inbuilt function in Python that returns the fractional and integer parts of the number in a two-item tuple. Both parts have the same sign as the number. The integer part is returned as a float.
In Python, there is a module called Decimal, which is used to do some decimal floating point related tasks. This module provides correctly-rounded floating point arithmetic. To use it at first we need to import it the Decimal standard library module. import decimal.
>>> d
Decimal('1E-14')
>>> '{:f}'.format(d)
'0.00000000000001'
As Brandon Rhodes pointed out PEP 3101 (which is the string format PEP) states:
The syntax for format specifiers is open-ended, since a class can override the standard format specifiers. In such cases, the str.format() method merely passes all of the characters between the first colon and the matching brace to the relevant underlying formatting method.
And thus, the Decimal.__format__
method is what python's string format will utilize to generate the str
representation of the Decimal
value. Basically Decimal
overrides the formatting to be "smart" but will default to whatever values the format string sets (ie {:.4f}
will truncate the decimal to 4 places).
Here's why you can trust it (snippet from decimal.py:Decimal.__format__
):
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)
Long story short, the Decimal.__format__
method will calculate the necessary padding to represent the number before and after the decimal based upon exponentiation provided from Decimal._exp
(in your example, 14 significant digits).
>>> d._exp
-14
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With