获取Python Decimal的精确十进制字符串表示?

问题描述 投票:0回答:2

如果我有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()
自己推出吗?

python decimal
2个回答
6
投票

简短回答:

>>> 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

0
投票

@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 相同。

© www.soinside.com 2019 - 2024. All rights reserved.