将float转换为位置格式的字符串(没有科学计数法和错误的精度) [英] Convert float to string in positional format (without scientific notation and false precision)

查看:285
本文介绍了将float转换为位置格式的字符串(没有科学计数法和错误的精度)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想打印一些浮点数,以便始终以十进制形式(例如12345000000000000000000.00.000000000000012345)而不是 IEEE 754两倍的有效数字,并且没有更多了.

I want to print some floating point numbers so that they're always written in decimal form (e.g. 12345000000000000000000.0 or 0.000000000000012345, not in scientific notation, yet I'd want to the result to have the up to ~15.7 significant figures of a IEEE 754 double, and no more.

我想要的是 理想地,这样结果是位置十进制格式的最短字符串,当转换为 .

What I want is ideally so that the result is the shortest string in positional decimal format that still results in the same value when converted to a float.

众所周知,如果指数大于15或小于-4,则以科学计数法表示floatrepr:

It is well-known that the repr of a float is written in scientific notation if the exponent is greater than 15, or less than -4:

>>> n = 0.000000054321654321
>>> n
5.4321654321e-08  # scientific notation

如果使用str,则生成的字符串再次采用科学计数法:

If str is used, the resulting string again is in scientific notation:

>>> str(n)
'5.4321654321e-08'


有人建议我可以将formatf标志一起使用,并具有足够的精度来摆脱科学记号:


It has been suggested that I can use format with f flag and sufficient precision to get rid of the scientific notation:

>>> format(0.00000005, '.20f')
'0.00000005000000000000'

它适用于该数字,尽管它有一些额外的尾随零.但是随后.1的相同格式失败,该格式给出的十进制数字超出了float的实际机器精度:

It works for that number, though it has some extra trailing zeroes. But then the same format fails for .1, which gives decimal digits beyond the actual machine precision of float:

>>> format(0.1, '.20f')
'0.10000000000000000555'

如果我的电话号码是4.5678e-20,则使用.20f仍然会失去相对精度:

And if my number is 4.5678e-20, using .20f would still lose relative precision:

>>> format(4.5678e-20, '.20f')
'0.00000000000000000005'

因此这些方法与我的要求不符.

这引发了一个问题:以十进制格式打印任意浮点数,与

This leads to the question: what is the easiest and also well-performing way to print arbitrary floating point number in decimal format, having the same digits as in repr(n) (or str(n) on Python 3), but always using the decimal format, not the scientific notation.

也就是说,例如将浮点值0.00000005转换为字符串'0.00000005'的函数或操作; 0.1'0.1'; 420000000000000000.0'420000000000000000.0'420000000000000000,并将浮点值-4.5678e-5格式化为'-0.000045678'.

That is, a function or operation that for example converts the float value 0.00000005 to string '0.00000005'; 0.1 to '0.1'; 420000000000000000.0 to '420000000000000000.0' or 420000000000000000 and formats the float value -4.5678e-5 as '-0.000045678'.

赏金期之后:似乎至少有2种可行的方法,正如Karin证明的那样,与我最初在Python 2上使用的算法相比,使用字符串操作可以显着提高速度.

After the bounty period: It seems that there are at least 2 viable approaches, as Karin demonstrated that using string manipulation one can achieve significant speed boost compared to my initial algorithm on Python 2.

因此

  • If performance is important and Python 2 compatibility is required; or if the decimal module cannot be used for some reason, then Karin's approach using string manipulation is the way to do it.
  • On Python 3, my somewhat shorter code will also be faster.

由于我主要是在Python 3上进行开发,因此我将接受自己的回答,并奖励Karin.

Since I am primarily developing on Python 3, I will accept my own answer, and shall award Karin the bounty.

推荐答案

不幸的是,似乎连float.__format__的新格式都不支持此功能. float的默认格式与repr相同.并带有f标志,默认情况下有6个小数位数:

Unfortunately it seems that not even the new-style formatting with float.__format__ supports this. The default formatting of floats is the same as with repr; and with f flag there are 6 fractional digits by default:

>>> format(0.0000000005, 'f')
'0.000000'


但是,有一种方法可以取得理想的结果-不是最快的方法,而是相对简单的方法:


However there is a hack to get the desired result - not the fastest one, but relatively simple:

  • 首先,使用str()repr()
  • 将浮点数转换为字符串
  • 然后从中创建一个新的 Decimal 实例该字符串.
  • Decimal.__format__支持提供所需结果的f标志,与float s不同,它打印实际精度而不是默认精度.
  • first the float is converted to a string using str() or repr()
  • then a new Decimal instance is created from that string.
  • Decimal.__format__ supports f flag which gives the desired result, and, unlike floats it prints the actual precision instead of default precision.

因此,我们可以创建一个简单的实用函数float_to_str:

Thus we can make a simple utility function 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')

必须注意不要使用全局十进制上下文,因此将为此函数构造一个新的上下文.这是最快的方法.另一种方法是使用decimal.local_context,但是会更慢,为每次转换创建一个新的线程本地上下文和上下文管理器.

Care must be taken to not use the global decimal context, so a new context is constructed for this function. This is the fastest way; another way would be to use decimal.local_context but it would be slower, creating a new thread-local context and a context manager for each conversion.

此函数现在返回带有尾数所有可能数字的字符串,四舍五入为最短等价表示形式:

This function now returns the string with all possible digits from mantissa, rounded to the shortest equivalent representation:

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

最后的结果四舍五入到最后一位

The last result is rounded at the last digit

正如@Karin所指出的,float_to_str(420000000000000000.0)与所期望的格式不严格匹配;它返回420000000000000000而没有尾随.0.

As @Karin noted, float_to_str(420000000000000000.0) does not strictly match the format expected; it returns 420000000000000000 without trailing .0.

这篇关于将float转换为位置格式的字符串(没有科学计数法和错误的精度)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆