将浮点格式设置为固定宽度的python

时间:2018-10-11 15:37:09

标签: python formatting

我不在乎小数点的位置,并且希望在适当的情况下以科学的符号进行打印以最大程度地显示。但是,我想适当地四舍五入以保持固定的宽度,向左破烂或向右破烂都可以。

例如

>>> my_format("{:10f}", 0.0000000456)
"  4.56e-08"
>>> my_format("{:10f}", 12.345678987654321)
" 12.345679"
#or "12.34567890" because significant digits
#but not "1.2346e+01", since it is less accurate than the above representations
>>> my_format("{:10f}", 12345678987654321)
"1.2345e+16"

编辑以澄清示例,width格式说明符未提供固定宽度。它提供了最小宽度。如何获得固定宽度的表示形式?

5 个答案:

答案 0 :(得分:3)

g格式说明符通常是不错的选择,如果您想使用科学记数法,即:

my_format = "{:.10g}".format

应该做正确的事情:

>>> list(my_format(v) for v in (0.0000000456, 12.345678987654321, 12345678987654321))
['4.56e-08', '12.34567899', '1.234567899e+16']

后来我意识到上述功能不能满足OP的要求

基于@a_guest的有用评论,我提出了以下建议:

def my_format(v, length=10):
    n = length
    while n > 0:
        i = len('%#.*g' % (n, v))
        s = '%.*g' % (n + n - i, v)
        if len(s) <= length:
            return s
        n -= 1
    return s

现在我得到['4.56e-08', '12.345679', '1.2346e+16']了,它更接近想要的东西。

我已经使用以下方法生成了大量随机数进行了测试:

from random import uniform
def rnd_float():
    return uniform(-10, 10) * 10 ** (uniform(-1.6, 1.6) ** 11)

相当随意,但是生成的数字与我所关心的分布相当接近。即大多在1附近,但有些很小或很大,几率还不错。

我已将数字从此传递给my_format 10万次,然后又获得了格式正确的数字。

答案 1 :(得分: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')

print float_to_str(0.0000000456)
print float_to_str(12.345678987654321)

答案 2 :(得分:1)

您可以测试将数字设置为{:f}{:e}的格式,然后解析生成的字符串以查看哪个更合适:

import re


def format(spec, number):
    def _decimal(s):
        return re.search(r'^\s*-?([0-9]+(\.[0-9]+)?)', s).group(1)

    def _significant_digits(s):
        return _decimal(s).rstrip('0')

    def _fit_to_width(s):
        decimal, significant = _decimal(s), _significant_digits(s)
        stripped = s.replace(decimal, significant)
        excess = len(stripped) - spec
        if excess > 0:
            # Replace excess digits from the right.
            significant = significant[::-1].replace(
                re.match(
                    r'[0-9]{{,{}}}'.format(excess),
                    significant[::-1]
                ).group(0), ''
            )[::-1]
        return s.replace(decimal, significant)

    formats = [
        _fit_to_width('{{:{}f}}'.format(spec).format(number)),
        _fit_to_width('{{:{}e}}'.format(spec).format(number)),
    ]
    return max(
        filter(
            lambda x: len(x[0]) <= spec,
            [(f, len(_significant_digits(f))) for f in formats]
        ),
        key=lambda x: x[-1]
    )[0].rjust(spec)


print(format(10, 0.0000000456))
print(format(10, 12.345678987654321))
print(format(10, 12345678987654321))

#   4.56e-08
#  12.345679
# 1.2345e+16

答案 3 :(得分:0)

这似乎有效。可以摆脱numpy,但是四舍五入需要更多的工作。

import numpy as np

SCIENTIFIC_NOTATION_WIDTH = 4

def my_format(number, n):
    places = np.log10(np.abs(number))
    if abs(places) == np.inf:
        places = 0
    highest_place = -int(places)
    if 1 <= highest_place < 3:
        rounded = np.round(number, n - highest_place - 1)
    elif highest_place >= 3:
        rounded = np.round(number, highest_place + n - 5)
    elif -n < highest_place < 1:
        rounded = np.round(number, n + highest_place - 2)
    else:
        rounded = np.round(number, highest_place + n - 6)

    return "{{:{}.{}g}}".format(n,n).format(rounded)
print(my_format(12345678987654321, 10))
print(my_format(12.345678987654321,10))
print(my_format(0.0000000456,10))

#1.2346e+16
# 12.345679
#  4.56e-08

答案 4 :(得分:0)

如果提供的长度对于给定的数字来说太小,例如,下面的代码可以完成该工作并产生更长的输出。只能使用四个字符表示def rolling_window( aMatrix, aRollingWindowLENGTH ): # # http://www.rigtorp.se/2011/01/01/rolling-statistics-numpy.html """ __doc__ USAGE: rolling_window( aMatrix, aRollingWindowLENGTH ) PARAMS: aMatrix a numpy array aRollingWindowLENGTH a LENGTH of a Rolling Window RETURNS: a stride_trick'ed numpy array with rolling windows THROWS: n/a EXAMPLE: >>> x = np.arange( 10 ).reshape( ( 2, 5 ) ) >>> rolling_window( x, 3 ) array([[[0, 1, 2], [1, 2, 3], [2, 3, 4]], [[5, 6, 7], [6, 7, 8], [7, 8, 9]]]) >>> np.mean( rolling_window( x, 3 ), -1 ) array([[ 1., 2., 3.], [ 6., 7., 8.]]) """ new_shape = aMatrix.shape[:-1] + ( aMatrix.shape[-1] - aRollingWindowLENGTH + 1, aRollingWindowLENGTH ) new_strides = aMatrix.strides + ( aMatrix.strides[-1], ) return np.lib.stride_tricks.as_strided( aMatrix, shape = new_shape, strides = new_strides )

4e100

产生

import math

def float_fmt(x, length=10):
  reduce_fp_by = 0

  if abs(x) > 0.0:
    abs_power = abs(math.floor(math.log10(abs(x))))
    if abs_power > 0:
      power_len = max(1, math.floor(math.log10(abs_power)))
      if abs_power > 4:
        reduce_fp_by = power_len + 3

  fp_n = max(0, length - reduce_fp_by - 1)
  fmt = ('%#.'+str(fp_n)+'g')
  return fmt % x

for l in [6, 8, 10, 13]:
  for bf in [0, 4.2, 4.222, 4.22222, 4.2222222]:
    for p in [-500, -100, -50, -5, 50, 100]:
      f = bf * (10.0 ** p)
      print(float_fmt(f, l), len(float_fmt(f, l)))