我如何格式化一个浮点数,使它不包含尾随零?换句话说,我希望得到的字符串尽可能短。

例如:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"

当前回答

这里有一个对我有用的解决办法。它混合了PolyMesh的解决方案和使用新的.format()语法。

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

输出:

3
3
3
3.1
3.14
3.14

其他回答

我呢,我会用('%f' % x).rstrip('0').rstrip('.')——保证定点格式,而不是科学符号,等等。是的,不像%g那么流畅和优雅,但是,它是有效的(而且我不知道如何强迫%g永远不使用科学符号;-)。

尝试一下,它将允许你添加一个“精度”变量来设置你想要的小数点后多少位。只要记住它会四舍五入。请注意,这只适用于字符串中有小数的情况。

 number = 4.364004650000000
 precision = 2
 result = "{:.{}f}".format(float(format(number).rstrip('0').rstrip('.')), precision)

输出

 4.364004650000000
 4.36

使用QuantiPhy包是一种选项。通常QuantiPhy用于 使用数字单位和SI比例因子,但它有各种 不错的数字格式选项。

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

在这种情况下,它不会使用e符号:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

您可能更喜欢的另一种选择是使用SI比例因子,可能带有单位。

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm

如果你想要一些既适用于数字输入又适用于字符串输入的东西(感谢@mike-placentra的bug搜索):

def num(s):
    """ 3.0 -> 3, 3.001000 -> 3.001 otherwise return s """
    s = str(s)
    try:
        int(float(s))
        if '.' not in s:
            s += '.0'
        return s.rstrip('0').rstrip('.')
    except ValueError:
        return s

>>> for n in [3, 3., 3.0, 3.1, 3.14, 3.140, 3.001000, 30 ]: print(num(n))
... 
3
3
3
3.1
3.14
3.14
3.001
30

>>> for n in [3, 3., 3.0, 3.1, 3.14, 3.140, 3.001000, 30 ]: print(num(str(n)))
... 
3
3
3
3.1
3.14
3.14
3.001
30

一个新的挑战者出现了。

def prettify_float(real: float, precision: int = 2) -> str:
    '''
    Prettify the passed floating-point number into a human-readable string,
    rounded and truncated to the passed number of decimal places.

    This converter prettifies floating-point numbers for human consumption,
    producing more readable results than the default :meth:`float.__str__`
    dunder method. Notably, this converter:

    * Strips all ignorable trailing zeroes and decimal points from this number
      (e.g., ``3`` rather than either ``3.`` or ``3.0``).
    * Rounds to the passed precision for perceptual uniformity.

    Parameters
    ----------
    real : float
        Arbitrary floating-point number to be prettified.
    precision : int, optional
        **Precision** (i.e., number of decimal places to round to). Defaults to
        a precision of 2 decimal places.

    Returns
    ----------
    str
        Human-readable string prettified from this floating-point number.

    Raises
    ----------
    ValueError
        If this precision is negative.
    '''

    # If this precision is negative, raise an exception.
    if precision < 0:
        raise ValueError(f'Negative precision {precision} unsupported.')
    # Else, this precision is non-negative.

    # String prettified from this floating-point number. In order:
    # * Coerce this number into a string rounded to this precision.
    # * Truncate all trailing zeroes from this string.
    # * Truncate any trailing decimal place if any from this string.
    result = f'{real:.{precision}f}'.rstrip('0').rstrip('.')

    # If rounding this string from a small negative number (e.g., "-0.001")
    # yielded the anomalous result of "-0", return "0" instead; else, return
    # this result as is.
    return '0' if result == '-0' else result

不要相信我的谎言

pytest风格的单元测试,否则就不会发生。

def test_prettify_float() -> None:
    '''
    Test usage of the :func:`prettify_float` prettifier.
    '''

    # Defer test-specific imports.
    from pytest import raises

    # Assert this function prettifies zero as expected.
    assert prettify_float(0.0) == '0'

    # Assert this function prettifies a negative integer as expected.
    assert prettify_float(-2.0) == '-2'

    # Assert this prettifier prettifies a small negative float as expected.
    assert prettify_float(-0.001) == '0'

    # Assert this prettifier prettifies a larger negative float as expected.
    assert prettify_float(-2.718281828) == '-2.72'
    assert prettify_float(-2.718281828, precision=4) == '-2.7183'

    # Assert this function prettifies a positive integer as expected.
    assert prettify_float(3.0) == '3'

    # Assert this function prettifies a positive float as expected.
    assert prettify_float(3.14159265359) == '3.14'
    assert prettify_float(3.14159265359, precision=4) == '3.1416'

    # Assert this prettifier raises the expected exception when passed a
    # negative precision.
    with raises(ValueError):
        prettify_float(2.718281828, precision=-2)

%100纯Python

忽略那些诱人的简单答案,比如:

琐碎的一行。它们在常见的边缘情况下都失败了,比如整数或小的负浮点数。 第三方包。NumPy, QuantiPhy和more_itertools?你肯定是在开玩笑。不要额外增加维护负担或代码债务。也就是说……

在prettify_float()上抛出@beartype,以增加运行时安全性,你就成功了!你的用户群会对你赞不绝口。那我也是,我很确定我的偏见在这里表现出来了。

另请参阅

这个答案站在巨大的猛犸象的肩膀上,包括:

亚历克斯·马尔泰利聪明的回答。 PolyMesh对Martelli答案的推广,以捕捉小负浮的边缘情况。 Kaushal Modi对PolyMesh的答案进行了概括,以强制实现小数点后两位的精度。