如果我有Python
Decimal
,我怎样才能可靠地获得数字的精确十进制字符串(即不是科学记数法)表示而不带尾随零?
例如,如果我有:
>>> d = Decimal('1e-14')
我想要:
>>> get_decimal_string(d)
'0.00000000000001'
但是:
Decimal
类没有任何to_decimal_string
方法,甚至没有任何to_radix_string(radix)
(参见:https://docs.python.org/3/library/decimal.html#decimal.Context.to_eng_string )%f
格式化程序默认四舍五入到小数点后 6 位 - '%f' %(d, ) ==> '0.000000'
- 或者需要精确的小数位数。{:f}.format(...)
格式化程序似乎可以工作 - '{:f}'.format(d)
==> '0.00000000000001'
- 但是我不愿意相信这一点,因为这实际上与文档背道而驰,文档中说“'f'
……将数字显示为定点数。默认精度为 6"Decimal.__repr__
和 Decimal.__str__
有时会返回科学计数法:repr(d) ==> "Decimal('1E-14')"
那么,有什么方法可以从Python
Decimal
获取十进制字符串吗?或者我需要使用 Decimal.as_tuple()
自己推出吗?
>>> d
Decimal('1E-14')
>>> '{:f}'.format(d)
'0.00000000000001'
正如 Brandon Rhodes 指出的 PEP 3101(这是字符串格式 PEP)指出:
格式说明符的语法是开放式的,因为类可以 覆盖标准格式说明符。在这种情况下, str.format() 方法仅传递 str.format() 之间的所有字符 第一个冒号和相关底层的匹配大括号 格式化方法。
因此,Python 的字符串格式将利用
Decimal.__format__
方法来生成 str
值的 Decimal
表示形式。 基本上 Decimal
会覆盖“智能”格式,但会默认为格式字符串设置的任何值(即 {:.4f}
会将小数截断至 4 位)。
这就是您可以信任它的原因(来自
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)
长话短说,
Decimal.__format__
方法将根据Decimal._exp
提供的幂(在您的示例中,14位有效数字)计算必要的填充来表示小数点前后的数字。
>>> d._exp
-14
@Julian 的 answer 提供了有关 Python 2 的
decimal.py:Decimal.__format__
的详细信息。
Python 3 中的答案更复杂,因为 decimal.py 只是本机模块的包装器:
from _decimal import *
__format__
由_decimal.c:dec_format
实现,它将调用_decimal/libmpdec/io.c:mpd_parse_fmt_str
来解析格式字符串。如果成功,它会调用 io.c:mpd_qformat_spec
来处理 e
格式设置:
case 'e':
if (mpd_iszero(dec)) {
dplace = 1-spec->prec;
}
else {
_mpd_round(&tmp, dec, spec->prec+1, ctx,
&workstatus);
dec = &tmp;
}
break;
如果
mpd_qformat_spec
失败,则调用 pydec_format
,这会调用 _pydecimal.py:Decimal.__format__
,其逻辑与 Python 2 相同。