格式化浮点数而不使用尾随零

2024-12-20 08:37:00
admin
原创
66
摘要:问题描述:如何格式化浮点数以使其不包含尾随零?换句话说,我希望生成的字符串尽可能短。例如:3 -> "3" 3. -> "3" 3.0 -> "3" 3.1 -> "3.1" 3.14 -> &quo...

问题描述:

如何格式化浮点数以使其不包含尾随零?换句话说,我希望生成的字符串尽可能短。

例如:

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

解决方案 1:

您可以使用%g来实现这一点:

'%g'%(3.140)

或者,使用 Python ≥ 2.6:

'{0:g}'.format(3.140)

或者,使用 Python ≥ 3.6:

f'{3.140:g}'

来自文档formatg原因(以及其他事项)

从有效数字中删除不重要的尾随零,并且如果小数点后没有剩余数字,则小数点也将被删除。

解决方案 2:

我会这样做('%f' % x).rstrip('0').rstrip('.')- 保证定点格式而不是科学计数法等等。是的,不像那么光滑和优雅%g,但是,它有效(并且我不知道如何强制%g永远不使用科学计数法;-)。

解决方案 3:

在查看了几个类似问题的答案之后,这似乎是对我来说最好的解决方案:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

我的理由是:

%g并没有摆脱科学计数法。

>>> '%g' % 0.000035
'3.5e-05'

15 位小数似乎可以避免奇怪的行为,并且足以满足我的需要。

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

format(inputValue, '.15f').我本可以使用'%.15f' % inputValue,但是那样会慢一点(~30%)。

我本可以使用Decimal(inputValue).normalize(),但这也存在一些问题。首先,它慢了很多 (~11 倍)。我还发现,虽然它的精度相当高,但使用 时仍然会遭受精度损失normalize()

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

最重要的是,我仍然会从 转换为Decimalfloat这可能会导致您最终得到除您输入的数字之外的其他值。我认为Decimal当算术保留在 中Decimal并且Decimal用字符串初始化时效果最好。

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

我确信可以使用上下文设置将精度问题Decimal.normalize()调整到需要的程度,但考虑到速度已经很慢并且不需要太高的精度,而且我仍然会从浮点数转换并失去精度,我认为这不值得追求。

我并不关心可能的“-0”结果,因为 -0.0 是一个有效的浮点数,而且它可能很少出现,但既然你确实提到你想让字符串结果尽可能短,你总是可以使用一个额外的条件,而额外的速度成本很少。

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result

解决方案 4:

那么尝试最简单、最有效的方法怎么样?方法normalize()删除所有最右边的尾随零。

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

适用于Python 2Python 3

-- 已更新 --

正如@BobStein-VisiBone 指出的那样,唯一的问题是 10、100、1000... 等数字将以指数表示形式显示。可以使用以下函数轻松修复此问题:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()

解决方案 5:

这是对我有用的解决方案。它结合了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

解决方案 6:

如果你想要一些可以同时处理数字和字符串输入的东西(感谢@mike-placentra 的漏洞搜寻):

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

解决方案 7:

您可以简单地使用 format() 来实现这一点:

format(3.140, '.10g')其中 10 是您想要的精度。

解决方案 8:

>>> str(a if a % 1 else int(a))

解决方案 9:

答案如下:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

输出“3.14”和“3”

trim='-'删除尾随零和小数。

解决方案 10:

一个新的挑战者已经出现。

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?你肯定在开玩笑。不要增加你的维护负担或代码债务。话虽如此……

投入额外@beartypeprettify_float()运行时安全性,你就大功告成了!你的用户群会对你大加赞赏。然后我也会。另外,我很确定我的偏见在这里有所体现。

参见

这个答案站在巨型猛犸象的肩膀上——其中包括:

  1. 亚历克斯马特利 (Alex Martelli ) 的聪明回答被接受。

  2. PolyMesh对 Martelli 的答案进行了概括,以捕捉小负浮点数的边缘情况。

  3. Kaushal Modi对PolyMesh 的答案进行了概括,强制精度达到小数点后两位。

解决方案 11:

虽然格式化可能是最 Pythonic 的方式,但这里有一个使用该more_itertools.rstrip工具的替代解决方案。

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))


assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

数字被转换为字符串,字符串中去掉了满足谓词的尾随字符。函数定义fmt不是必需的,但在这里使用它来测试断言,所有断言都通过了。注意:它适用于字符串输入并接受可选谓词。

另请参阅有关此第三方库的详细信息more_itertools

解决方案 12:

对于浮点数,你可以使用这个:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

测试一下:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

对于十进制,请参阅此处的解决方案: https: //stackoverflow.com/a/42668598/5917543

解决方案 13:

OP 希望删除多余的零并使生成的字符串尽可能短。

我发现 %g 指数格式会缩短非常大和非常小的值的结果字符串。问题出现在不需要指数表示法的值上,例如 128.0,它既不是非常大也不是非常小。

以下是将数字格式化为短字符串的一种方法,仅当 Decimal.normalize 创建的字符串太长时才使用 %g 指数表示法。这可能不是最快的解决方案(因为它确实使用了 Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']

解决方案 14:

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

在这种情况下它不会使用电子符号:

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

解决方案 15:

尝试一下,它将允许您添加一个“精度”变量来设置您想要的小数位数。只需记住它会向上舍入。请注意,这仅在字符串中有小数时才有效。

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

输出

 4.364004650000000
 4.36

解决方案 16:

你可以max()这样使用:

print(max(int(x), x))

解决方案 17:

"{:.5g}".format(x)

我使用它将浮点数格式化为尾随零。

解决方案 18:

你可以用最符合 Python 风格的方式实现这一点,如下所示:

python3:

"{:0.0f}".format(num)

解决方案 19:

处理 %f 你应该把

%.2f

,其中: .2f == .00 浮点数。

例子:

打印“价格:%.2f” % prices[product]

输出:

价格:1.50

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1019  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨职能团队协作为基础的产品开发方法。它通过整合市场、研发、制造、供应链等各个环节的资源与信息,实现高效的产品开发流程。IPD不仅是一种方法论,更是一种系统化的管理思维,旨在缩短产品开发周期、降低开发成本、提高产品...
IPD培训课程   0  
  华为的IPD(集成产品开发)流程是全球范围内备受认可的产品开发管理体系,其核心在于通过跨部门协作和系统化的流程管理,提升产品开发效率和质量。在IPD流程中,团队建设与领导力培养是两个至关重要的环节。高效的团队能够确保项目顺利推进,而优秀的领导力则是团队凝聚力和执行力的保障。本文将从团队建设的重要性、领导力在IPD中的核...
IPD集成产品开发流程   0  
  华为的集成产品开发(IPD)流程是其成功的关键因素之一,它不仅提升了产品开发的效率,还通过系统化的风险管理机制确保了项目的顺利推进。在IPD流程中,风险管理被视为贯穿始终的核心环节,其目的是在项目初期识别潜在问题,并在整个开发周期中持续监控和应对风险。通过有效的风险管理,华为能够最大限度地减少项目延误、成本超支和质量问...
IPD结构化流程   0  
  在项目管理领域,CDCP(Critical Decision Control Point)评审是确保项目成功的关键环节之一。CDCP评审的核心在于通过系统化的决策流程,确保项目在每个关键节点都能做出正确的选择,从而降低风险、提高效率并最终实现项目目标。然而,许多项目团队在CDCP评审过程中常常面临决策效率低下、信息不对...
华为IPD流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用