获取Python Decimal的精确小数字符串表示?

7

如果我有一个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个回答

6

简短回答:

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

长答案:

正如Brandon Rhodes所指出的PEP 3101(即字符串格式化PEP)中所述:

格式化说明符的语法是开放式的,因为一个类可以覆盖标准的格式化说明符。在这种情况下,str.format()方法只是将第一个冒号和匹配的花括号之间的所有字符传递给相关的底层格式化方法。

因此,Decimal.__format__方法是Python字符串格式化将利用生成Decimal值的str表示的方法。基本上,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个有效数字)。请注意保留 HTML 标记。
>>> d._exp
-14

你引用的文档仅涉及内部表示,而非format将要生成的内容。 - Mark Ransom
请看我上面的第三点:这似乎是有效的,但它与 string.format 文档关于 'f' 行为的描述相矛盾,因此我不太敢相信它。 - David Wolever
2
如果有疑问:查看源代码,哈哈。我会更新答案,但基本上你可以信任它,因为Decimal.__format__方法的实现。 - Julian
我建议查看 decimal.py 的源代码,因为其格式非常独特。可惜文档中没有提到这一点。 - Julian
1
请记住,string.format 不再控制格式操作符所执行的操作 — 现在这个任务被延迟到了 __format__() 方法中。 - Brandon Rhodes
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接