格式化没有尾随零的浮点数

Posted

技术标签:

【中文标题】格式化没有尾随零的浮点数【英文标题】:Formatting floats without trailing zeros 【发布时间】:2011-01-27 07:05:37 【问题描述】:

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

例如:

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

【问题讨论】:

这个例子根本没有任何意义。 3.14 == 3.140 -- 它们是 same 浮点数。就此而言,3.140000 是相同的浮点数。零一开始就不存在。 @S.Lott - 我认为问题在于打印没有尾随零的浮点数,而不是两个数字的实际等价。 @pokstad:在这种情况下,没有“多余的”零。 %0.2f%0.3f 是生成左侧最后一个数字所需的两种格式。使用%0.2f 生成右侧的最后两个数字。 3.0 -> "3" 仍然是一个有效的用例。 print( ':,g'.format( X ) 为我工作以输出 3 其中 X = 6 / 2 和当 X = 5 / 2 我得到 2.5 的输出时,如预期的那样。 老问题,但是.. print("%s"%3.140) 给你你想要的。 (我在下面添加了一个答案......) 【参考方案1】:

我,我会做 ('%f' % x).rstrip('0').rstrip('.') -- 保证定点格式而不是科学记数法等。是的,不像 %g 那样流畅和优雅,但是,它有效(我不知道如何强制%g 从不使用科学记数法;-)。

【讨论】:

唯一的问题是'%.2f' % -0.0001 会留下-0.00 和最终-0 @alexanderlukanin13 因为默认精度为 6,请参阅 docs.python.org/2/library/string.html: 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6. 在上述解决方案中您必须使用 '%0.7f'。 @derenio 好点 :-) 我只能补充说,将精度提高到 '%0.15f' 以上是个坏主意,因为 weird stuff 开始发生了。 如果您处于其他字符串的中间:print('In the middle and something else'.format(':f'.format(a).rstrip('0'))) @Peter Schorn:你说得对,Gabriel Staples 的优化很糟糕,但这是因为 OP 的技巧要求你先删除所有零,然后删除所有小数,然后再删除零。 Gabriel 的方法只是删除所有零和句点,直到遇到其他问题。【参考方案2】:

您可以使用%g 来实现:

'%g'%(3.140)

或者,Python ≥ 2.6:

'0:g'.format(3.140)

或者,Python ≥ 3.6:

f'3.140:g'

来自docs for formatg 原因(除其他外)

微不足道的尾随零 [to be] 从有效数字中删除,并且 如果有小数点也会被删除 后面没有剩余的数字。

【讨论】:

哦,差不多!有时它以科学计数法(“2.342E+09”)格式化浮点数 - 是否可以将其关闭,即始终显示所有有效数字? 既然可以使用format(value, '...'),为什么还要使用'0:...'.format(value)?这避免了必须从原本为空的模板字符串中解析出格式说明符。 @MartijnPieters:解析格式说明符的微不足道的成本被其他开销 AFAICT 所淹没;在实践中,我在 3.6 上的本地基准测试(具有微基准的功能范围以准确模拟真实代码)具有 format(v, '2.5f')':2.5f'.format(v) 长约 10%。即使没有,我也倾向于使用str 方法形式,因为当我需要对其进行调整、为其添加附加值等时,可更改的内容较少。当然,从 3.6 开始,我们有用于大多数用途的 f 弦。 :-) 在 Python 3.6 中,这可以缩短为 f"var:g",其中 var 是一个浮点变量。 @TarGz :我在'%d'%numpy.rint(my_number) 上取得了成功。基本上它的作用是使用 numpy 的 rint 方法将数字四舍五入到最接近的整数(您也可以使用 round),然后使用 int 标志 (%d) 打印它。您可以直接使用'%d'%my_number,但它会向下舍入数字而不是四舍五入到最接近的数字。【参考方案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(),但这也有一些问题。一方面,它慢了很多(~11x)。我还发现虽然它的精度非常高,但在使用normalize()时仍然会出现精度损失。

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

最重要的是,我仍然会从 float 转换为 Decimal,这可能会让你得到的结果不是你输入的数字。我认为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

【讨论】:

不幸的是,仅适用于小数点左侧少于大约 5 位或更多位的数字。例如,floatToString(12345.6) 返回'12345.600000000000364'。在此示例中,将%.15f 中的 15 减少到一个较小的数字即可解决此问题,但随着数字变大,该值需要越来越小。它可以根据数字的 log-base-10 动态计算,但这很快就会变得非常复杂。 解决该问题的一种方法可能是限制整数的长度(而不仅仅是小数点后的数字):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ') @JohnSpeeks 我不确定这是可以避免的。如果左侧需要更多数字,这是浮点数无法表示准确性的副作用。据我所知,作为字符串输出的数字与作为浮点数输入的数字相同,或者至少是最接近的表示形式。 >>>12345.600000000000364 == 12345.6True 我写了另一个solution。【参考方案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()

【讨论】:

除了Decimal('10.0').normalize()变成'1E+1'【参考方案5】:

这是一个对我有用的解决方案。它融合了 PolyMesh 的 solution 和新的 .format() syntax。

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

【讨论】:

唯一的问题是你必须设置一个合理的小数位数。设置得越高,可以表示的数字就越精确,但如果经常这样做,可能会降低性能。 添加到 beruic 的评论中,这不适用于精度更高的浮点数(例如 3.141),因为 .2f 是硬编码的。 result = ":.f".format(float(format(number).rstrip('0').rstrip('.')),precision),修复了这个问题TrebledJ。 非常适合一次性使用的应用程序,没有额外的库【参考方案6】:

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

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

【讨论】:

【参考方案7】:

虽然格式化可能是大多数 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

【讨论】:

这里的大多数解决方案(包括这个)完全忘记了以 0 结尾的整数,这是一种不受欢迎的行为。【参考方案8】:
>>> str(a if a % 1 else int(a))

【讨论】:

你不是说int(a) if a % 1 else a吗? 亲爱的 Beruic,您的回答结果是否定的。 a if a % 1 else int(a) 是正确的。问题需要以字符串形式输出,所以我只添加了str 啊,我明白了。 a % 1 是真实的,因为它不为零。我含蓄而错误地将其视为a % 1 == 0【参考方案9】:

如果您想要同时适用于数字或字符串输入的东西:

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

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

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

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

答案如下:

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='-' 删除尾随的零和小数点。

【讨论】:

使用 GIANT 库只实现单一功能是不明智的。【参考方案12】:

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:.1g'.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']

【讨论】:

【参考方案13】:

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

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://***.com/a/42668598/5917543

【讨论】:

【参考方案14】:

如果您可以忍受 3. 和 3.0 显示为“3.0”,这是一种非常简单的方法,可以从浮点表示中右剥离零:

print("%s"%3.140)

(感谢@ellimilial 指出例外情况)

【讨论】:

但是print("%s"%3.0) 可以。【参考方案15】:

试试这个,它可以让你添加一个“精度”变量来设置你想要的小数位数。请记住,它会四舍五入。请注意,这仅在字符串中有小数时才有效。

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

输出

 4.364004650000000
 4.36

【讨论】:

【参考方案16】:

使用宽度足够大的 %g,例如 '%.99g'。 它将以定点表示法打印任何相当大的数字。

编辑:它不起作用

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'

【讨论】:

.99 是精度,而不是宽度;有点用,但你不能以这种方式设置实际精度(除了自己截断它)。【参考方案17】:

你可以像这样使用max()

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

【讨论】:

您必须考虑x 是否定的情况。 if x < 0: print(min(x), x)else : print(max(x), x) 当我想做 json stringify 时一个有用的方法。 float 1.0 更改为 int 1,因此它的执行与 javascript 中的相同。【参考方案18】:

":.5g".format(x)

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

【讨论】:

3143.93 --> 3.1e+03【参考方案19】:

您可以像这样以大多数 Pythonic 方式实现这一点:

python3:

":0.0f".format(num)

【讨论】:

你是对的。最简单的方法是使用 ":g".format(num)【参考方案20】:

处理 %f 你应该把

%.2f

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

例子:

打印“价格:%.2f”% 价格[产品]

输出:

价格:1.50

【讨论】:

这明确说明不是问题想要的。需要投反对票。

以上是关于格式化没有尾随零的浮点数的主要内容,如果未能解决你的问题,请参考以下文章

c++中如何输出保留三位小数的浮点数

如何在java中格式化像0#.##这样的浮点数? [复制]

将 VarChar 转换为具有不同文化格式的浮点数

java编程把任意输入的浮点数按小数点后2位输出格式输出

打乱一个浮点数?

MODBUS RTU协议中浮点数是如何存储,读到浮点数寄存器的数值如何转换成所需的浮点数