【问题标题】:Get precise decimal string representation of Python Decimal?获取 Python Decimal 的精确十进制字符串表示?
【发布时间】:2019-01-02 21:48:55
【问题描述】:

如果我有一个 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' - 然而我不愿意相信它,因为这实际上与 the documentation 背道而驰,它说“'f' ... 将数字显示为定点数。默认精度为 6”
  4. Decimal.__repr__Decimal.__str__ 有时会返回科学计数法:repr(d) ==> "Decimal('1E-14')"

那么,有没有办法从 Python Decimal 获取十进制字符串?还是我需要使用Decimal.as_tuple() 自己推出?

【问题讨论】:

    标签: python decimal


    【解决方案1】:

    简答:

    >>> 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__ 的sn-p):

    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
    

    【讨论】:

    • 您引用的文档仅涉及内部表示,而不是 format 将产生的内容。
    • 对,这就是我说不清楚的原因。它仅通过示例暗示格式化将按预期工作。没有明确讨论与字符串的 .format 方法的交互。我会继续寻找,如果我发现了什么,我会发布。这些文档的另一个有趣部分:“与基于硬件的二进制浮点不同,十进制模块具有用户可更改的精度(默认为 28 位),它可以根据给定问题的需要而定:”但这个值似乎并不影响字符串格式。
    • 见我上面的第 3 点:这 appears 工作,但它与 string.format 文档中关于 'f' 的行为的说法相矛盾,所以我不愿意相信它。
    • 如有疑问:使用源代码,哈哈。我会更新答案,但基本上你可以信任它,因为 Decimal.__format__ 方法实现。
    • 请记住,string.format 无法控制格式运算符实际执行的操作——它现在被推迟到 __format__() 方法。
    猜你喜欢
    • 2018-08-07
    • 2017-07-21
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 2013-08-20
    • 1970-01-01
    相关资源
    最近更新 更多