Python:简洁的浮点格式而不丢失数量级

问题描述 投票:0回答:2

为了方便一目了然的比较,我的项目中的统计结果通常最好采用

0.3f
的格式,除了(a)小数位无用,或(b)有一个小分数,前三位都是零。从 Python 3.11.8 开始,似乎没有一种内置格式可以始终给出数量级,同时最大限度地减少无用数字。对标准格式的每个字符使用循环,可以调整结果以满足这些要求,但似乎效率低下且不必要的详细:

class FloatFormatter:
    def within(n, minimum, maximum):
        s = '{:.{m}f}'.format(n, m=maximum)
        decimal = truncate = False
        i = 0
        for c in s:
            i += 1
            match c:
                case '0':
                    continue
                case '.':
                    dot = i
                    decimal = True
                case _:
                    if (decimal):
                        truncate = True
                        break
        if decimal:
            places = (i - dot)
            if places < minimum:
                return s[:dot+minimum].rstrip('0') # minimum precision modulo trailing zeros
            if truncate:
                return s[:i].rstrip('0') # precision up to first non-zero
            if n > 1.0:
                return str(int(n)) + '.0_'
            else:
                return '* ' + '{:.0e}'.format(n)[1:] # sci-fi format with only the order of magnitude
        else:
            return s.rstrip('0').rstrip('.') # just the integer

用途:

>>> x = 1.23456
>>> print("• x: " + FloatFormatter.within(x, 3, 10))
• x: 1.234

特殊情况 1:显示额外的小数位(最多 10 位)以保留数量级

>>> x = 1.000023456
>>> print("• x: " + FloatFormatter.within(x, 3, 10))
• x: 1.00002

特殊情况 2:精度 10 不足以达到非零,所以只需表明它略高于零

>>> x = 1.00000000000023456
>>> print("• x: " + FloatFormatter.within(x, 3, 10))
• x: 1.0_

特殊情况3:保留数量级而不提供更多细节

>>> x = 0.00000000000023456
>>> print("• x: " + FloatFormatter.within(x, 3, 10))
• x: * e-13

是否有更简单的方法来实现相同的基本想法?

python floating-point format precision
2个回答
0
投票

您是否正在寻找重要的数字格式?

nums = np.random.uniform(-1, 1, 10)**6
for n in nums:
    print(f'{n:.3g}')

0.531
0.0515
0.000998
0.0097
0.235
0.000131
1.9e-06
1.62e-06
0.0133
3.99e-05

0
投票

您可以使用您开始使用的类为浮点数构建自定义格式化程序。 不要每次都传入最小和最大小数位,而是设置一次,然后让对象引用这些值。

您还可以重写

__add__
__radd__
方法来简化对象的使用,将其添加到浮点数会返回格式化字符串。

class FloatFormatter:
    def __init__(self, min_sigfig: int=3, max_sigfig: int=10):
        self.min_sigfig = min_sigfig
        self.max_sigfig = max_sigfig

    def format(self, x: float) -> str:
        i = int(x)
        d = (x - i) if i >= 0 else -(x - i)
        dstr = str(d).replace('.','').lstrip('0')
        n = -math.log10(d) if d>0.0 else 0.1
        # handle exact power of 10
        if n.is_integer():
            n -= 1.0
        n = int(n)
  
        if n <= self.min_sigfig:
            suffix = '0'*n + dstr[:self.min_sigfig-n]
        elif n <= self.max_sigfig:
            suffix = '0'*n + dstr[0]
        else:
            suffix = '_'
        return f'{i}.{suffix}'

    def __add__(self, other: float):
        return self.format(other)

    def __radd__(self, other: float):
        return self.format(other)

ff = FloatFormatter()

要使用格式化程序,只需将其添加到浮动中即可:

>>> 1.012345 + ff
'1.012'
>>> 1.34567 + ff
'1.345'
>>> 1.00897 + ff
'1.008'
>>> 1.000000222 + ff
'1.0000002`
>>> 1.00000000000643 + ff
'1._'
>>> 1.0000000000000000000000000888 + ff  # this is beyond the precision limit
'1.'
© www.soinside.com 2019 - 2024. All rights reserved.