格式化浮点数而不使用尾随零
- 2024-12-20 08:37:00
- admin 原创
- 66
问题描述:
如何格式化浮点数以使其不包含尾随零?换句话说,我希望生成的字符串尽可能短。
例如:
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}'
来自文档format
:g
原因(以及其他事项)
从有效数字中删除不重要的尾随零,并且如果小数点后没有剩余数字,则小数点也将被删除。
解决方案 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')
最重要的是,我仍然会从 转换为Decimal
,float
这可能会导致您最终得到除您输入的数字之外的其他值。我认为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 2和Python 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
?你肯定在开玩笑。不要增加你的维护负担或代码债务。话虽如此……
投入额外@beartype
的prettify_float()
运行时安全性,你就大功告成了!你的用户群会对你大加赞赏。然后我也会。另外,我很确定我的偏见在这里有所体现。
参见
这个答案站在巨型猛犸象的肩膀上——其中包括:
亚历克斯马特利 (Alex Martelli ) 的聪明回答被接受。
PolyMesh对 Martelli 的答案进行了概括,以捕捉小负浮点数的边缘情况。
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
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理必备:盘点2024年13款好用的项目管理软件