我如何格式化一个浮点数,使它不包含尾随零?换句话说,我希望得到的字符串尽可能短。
例如:
3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
我如何格式化一个浮点数,使它不包含尾随零?换句话说,我希望得到的字符串尽可能短。
例如:
3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
当前回答
一个新的挑战者出现了。
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的答案进行了概括,以强制实现小数点后两位的精度。
其他回答
你可以使用%g来实现:
'%g'%(3.140)
或者,Python≥2.6:
'{0:g}'.format(3.140)
或者,Python≥3.6:
f'{3.140:g}'
格式:g cause (among other things)
不重要的后面的零[是] 从意义上移除,和 如果有,小数点也会被移除 后面没有剩余数字。
我呢,我会用('%f' % x).rstrip('0').rstrip('.')——保证定点格式,而不是科学符号,等等。是的,不像%g那么流畅和优雅,但是,它是有效的(而且我不知道如何强迫%g永远不使用科学符号;-)。
虽然格式化可能是最python的方式,但这里有一个使用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的详细信息。
这里有一个对我有用的解决办法。它混合了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
对于float,你可以使用这个:
def format_float(num):
return ('%i' if num == int(num) else '%s') % num
测试:
>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'
Decimal参见解决方案:https://stackoverflow.com/a/42668598/5917543