Ant*_*ala 58 python floating-point python-2.x number-formatting python-3.x
我想打印一些浮点数,以便它们总是以十进制形式写入(例如,12345000000000000000000.0或者0.000000000000012345不是科学记数法,但我想保持15.7十进制数字的精度而不是更多.
众所周知,如果指数大于15或小于-4 ,则floata repr用科学记数法写成:
>>> n = 0.000000054321654321
>>> n
5.4321654321e-08 # scientific notation
Run Code Online (Sandbox Code Playgroud)
如果float使用,则再次生成的字符串采用科学计数法:
>>> str(n)
'5.4321654321e-08'
Run Code Online (Sandbox Code Playgroud)
有人建议我可以用str用format标志和足够的精度摆脱了科学计数法:
>>> format(0.00000005, '.20f')
'0.00000005000000000000'
Run Code Online (Sandbox Code Playgroud)
它适用于该数字,但它有一些额外的尾随零.但是相同的格式失败了f,它给出了超出float的实际机器精度的十进制数字:
>>> format(0.1, '.20f')
'0.10000000000000000555'
Run Code Online (Sandbox Code Playgroud)
如果我的号码是.1,使用4.5678e-20仍然会失去相对精确度:
>>> format(4.5678e-20, '.20f')
'0.00000000000000000005'
Run Code Online (Sandbox Code Playgroud)
因此,这些方法与我的要求不符.
这导致了一个问题:以十进制格式打印任意浮点数的最简单且性能最好的方法是什么,具有与.20f(或repr(n)在Python 3中)相同的数字,但总是使用小数格式,而不是科学记数法.
也就是说,例如将浮点值转换str(n)为字符串的函数或操作0.00000005; '0.00000005'到0.1; '0.1'to 420000000000000000.0或'420000000000000000.0'将float值格式化420000000000000000为-4.5678e-5.
在赏金期之后:似乎至少有两种可行的方法,因为Karin证明使用字符串操作与我在Python 2上的初始算法相比可以实现显着的速度提升.
从而,
'-0.000045678'某种原因无法使用模块,那么使用字符串操作的Karin方法就是这样做的.由于我主要在Python 3上开发,我将接受我自己的答案,并将奖励Karin赏金.
Ant*_*ala 44
不幸的是,似乎甚至没有float.__format__支持这种新式格式化.floats 的默认格式与with相同repr; 并且带有f标志默认有6个小数位数:
>>> format(0.0000000005, 'f')
'0.000000'
Run Code Online (Sandbox Code Playgroud)
然而,有一个黑客可以获得理想的结果 - 不是最快的结果,而是相对简单:
str()或将float转换为字符串repr()Decimal从该字符串创建一个新实例.Decimal.__format__支持f提供所需结果的标志,与floats 不同,它打印实际精度而不是默认精度.因此我们可以做一个简单的效用函数float_to_str:
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
Run Code Online (Sandbox Code Playgroud)
必须注意不要使用全局小数上下文,因此为此函数构造了新的上下文.这是最快的方式; 另一种方法是使用decimal.local_context但速度较慢,为每次转换创建一个新的线程局部上下文和上下文管理器.
此函数现在返回包含尾数中所有可能数字的字符串,四舍五入到最短的等效表示:
>>> float_to_str(0.1)
'0.1'
>>> float_to_str(0.00000005)
'0.00000005'
>>> float_to_str(420000000000000000.0)
'420000000000000000'
>>> float_to_str(0.000000000123123123123123123123)
'0.00000000012312312312312313'
Run Code Online (Sandbox Code Playgroud)
最后一个结果在最后一位数字处舍入
正如@Karin所说,float_to_str(420000000000000000.0)并不严格符合预期的格式; 它返回420000000000000000没有尾随.0.
Kar*_*rin 32
如果您对科学记数法的精确度感到满意,那么我们可以采用简单的字符串操作方法吗?也许它不是非常聪明,但似乎有效(通过了你提交的所有用例),我认为这是可以理解的:
def float_to_str(f):
float_string = repr(f)
if 'e' in float_string: # detect scientific notation
digits, exp = float_string.split('e')
digits = digits.replace('.', '').replace('-', '')
exp = int(exp)
zero_padding = '0' * (abs(int(exp)) - 1) # minus 1 for decimal point in the sci notation
sign = '-' if f < 0 else ''
if exp > 0:
float_string = '{}{}{}.0'.format(sign, digits, zero_padding)
else:
float_string = '{}0.{}{}'.format(sign, zero_padding, digits)
return float_string
n = 0.000000054321654321
assert(float_to_str(n) == '0.000000054321654321')
n = 0.00000005
assert(float_to_str(n) == '0.00000005')
n = 420000000000000000.0
assert(float_to_str(n) == '420000000000000000.0')
n = 4.5678e-5
assert(float_to_str(n) == '0.000045678')
n = 1.1
assert(float_to_str(n) == '1.1')
n = -4.5678e-5
assert(float_to_str(n) == '-0.000045678')
Run Code Online (Sandbox Code Playgroud)
表现:
我担心这种方法可能太慢,所以我运行timeit并与OP的十进制上下文解决方案进行比较.看起来字符串操作实际上要快得多.编辑:在Python 2中似乎只有更快.在Python 3中,结果是相似的,但使用小数方法稍快.
结果:
Python 2:使用ctx.create_decimal():2.43655490875
Python 2:使用字符串操作: 0.305557966232
Python 3:使用ctx.create_decimal():0.19519368198234588
Python 3:使用字符串操作: 0.2661344590014778
这是时间码:
from timeit import timeit
CODE_TO_TIME = '''
float_to_str(0.000000054321654321)
float_to_str(0.00000005)
float_to_str(420000000000000000.0)
float_to_str(4.5678e-5)
float_to_str(1.1)
float_to_str(-0.000045678)
'''
SETUP_1 = '''
import decimal
# create a new context for this task
ctx = decimal.Context()
# 20 digits should be enough for everyone :D
ctx.prec = 20
def float_to_str(f):
"""
Convert the given float to a string,
without resorting to scientific notation
"""
d1 = ctx.create_decimal(repr(f))
return format(d1, 'f')
'''
SETUP_2 = '''
def float_to_str(f):
float_string = repr(f)
if 'e' in float_string: # detect scientific notation
digits, exp = float_string.split('e')
digits = digits.replace('.', '').replace('-', '')
exp = int(exp)
zero_padding = '0' * (abs(int(exp)) - 1) # minus 1 for decimal point in the sci notation
sign = '-' if f < 0 else ''
if exp > 0:
float_string = '{}{}{}.0'.format(sign, digits, zero_padding)
else:
float_string = '{}0.{}{}'.format(sign, zero_padding, digits)
return float_string
'''
print(timeit(CODE_TO_TIME, setup=SETUP_1, number=10000))
print(timeit(CODE_TO_TIME, setup=SETUP_2, number=10000))
Run Code Online (Sandbox Code Playgroud)
use*_*ica 13
从NumPy 1.14.0开始,您可以使用numpy.format_float_positional。例如,针对您问题的输入:
>>> numpy.format_float_positional(0.000000054321654321)
'0.000000054321654321'
>>> numpy.format_float_positional(0.00000005)
'0.00000005'
>>> numpy.format_float_positional(0.1)
'0.1'
>>> numpy.format_float_positional(4.5678e-20)
'0.000000000000000000045678'
Run Code Online (Sandbox Code Playgroud)
numpy.format_float_positional使用Dragon4算法以位置格式生成最短的十进制表示形式,该格式将往返返回原始浮点输入。还有一种numpy.format_float_scientific科学的表示法,这两个函数都提供了可选参数来自定义诸如舍入和修剪零的东西。
如果您准备通过调用str()浮点数来丢掉任意精度,则可以采用以下方法:
import decimal
def float_to_string(number, precision=20):
return '{0:.{prec}f}'.format(
decimal.Context(prec=100).create_decimal(str(number)),
prec=precision,
).rstrip('0').rstrip('.') or '0'
Run Code Online (Sandbox Code Playgroud)
它不包括全局变量,允许您自己选择精度。选择小数精度100作为str(float)长度的上限。实际的最高要低得多。该or '0'部分适用于数量少且精度为零的情况。
请注意,它仍然有其后果:
>> float_to_string(0.10101010101010101010101010101)
'0.10101010101'
Run Code Online (Sandbox Code Playgroud)
否则,如果精度很重要,format就可以了:
import decimal
def float_to_string(number, precision=20):
return '{0:.{prec}f}'.format(
number, prec=precision,
).rstrip('0').rstrip('.') or '0'
Run Code Online (Sandbox Code Playgroud)
它不会丢失调用时丢失的精度str(f)。的or
>> float_to_string(0.1, precision=10)
'0.1'
>> float_to_string(0.1)
'0.10000000000000000555'
>>float_to_string(0.1, precision=40)
'0.1000000000000000055511151231257827021182'
>>float_to_string(4.5678e-5)
'0.000045678'
>>float_to_string(4.5678e-5, precision=1)
'0'
Run Code Online (Sandbox Code Playgroud)
无论如何,最大小数位是有限制的,因为float类型本身有其限制并且不能表示很长的浮点数:
>> float_to_string(0.1, precision=10000)
'0.1000000000000000055511151231257827021181583404541015625'
Run Code Online (Sandbox Code Playgroud)
另外,整数按原样格式化。
>> float_to_string(100)
'100'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
28505 次 |
| 最近记录: |