基于整数和小数部分的位数的十进制格式

时间:2013-12-31 12:10:19

标签: python python-3.x string-formatting

根据整数和小数部分的位数来设置十进制格式的最简单方法是什么?让我们说用户选择看4个数字,然后我有这个测试用例:

[('11111.1', '11111'),    # Integer part has >= 4 digits keep only integer part
 ('111.11', '111.1'),     # Integer part has < 4 digits keep total 4 digits
 ('11.111', '11.11'),    
 ('1.1111', '1.111'),
 ('0.00111', '0.0011'),   # Integer part has no digits keep 4 decimal part digits
 ('0.000011', '0.00001')] # First not zero digit place is > 4 keep first digit

解决方案应该允许选择要查看的位数。

更新目前我使用的解决方案不允许选择要查看的位数:

if value >= 1000:
    return '%d' % int(round(value, 0))
elif value >= 100:
    return '%.01f' % value
elif value >= 10:
    return '%.02f' % value
...

3 个答案:

答案 0 :(得分:1)

您只需使用嵌套格式参数调用str.format即可完成所有操作:

def format_float(value, ndigits=4):
    j = min(i-1 for i in range(ndigits+1) if value >= 10 ** (ndigits - i))
    return '{:.0{num_digits}f}'.format(value, num_digits=max(0,j))

这个想法是小数位数应该等于最小i,使得该值大于10 ^ (ndigits -i)。如果指定0的小数位数,则数字四舍五入为整数,因此输出正确。

示例用法:

In [66]: inputs = [11111.1, 111.11, 11.111, 1.1111]

In [67]: for inp in inputs:
    ...:     print('Input = {} -- output = {}'.format(inp, format_float(inp, 4)))
Input = 11111.1 -- output = 11111
Input = 111.11 -- output = 111.1
Input = 11.111 -- output = 11.11
Input = 1.1111 -- output = 1.111

然而,输入较小时会失败:

In [68]: format_float(0.0001, 4)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-68-68e3461a51e5> in <module>()
----> 1 format_float(0.0001, 4)

<ipython-input-64-d4485ac4e1c9> in format_float(value, ndigits)
      1 def format_float(value, ndigits=4):
----> 2     j = min(i-1 for i in range(ndigits+1) if value >= 10 ** (ndigits - i))
      3     return '{:.0{num_digits}f}'.format(value, num_digits=max(0,j))

ValueError: min() arg is an empty sequence

为了达到你想要的小数字,我们必须做一些更复杂的事情,检查小数位数。在这种情况下,只需将数字转换为字符串并剪切不需要的位置就更简单。

尝试可能是:

def format_float(value, ndigits=4):
    try:
        j = min(i-1 for i in range(ndigits+1) if value >= 10 ** (ndigits - i))
        return '{:.0{num_digits}f}'.format(value, num_digits=max(0,j))
    except ValueError:
        s = '{:.20f}'.format(value).rstrip('0')
        _, dec_part = s.split('.')
        if len(dec_part) > ndigits:
            if set(dec_part[:ndigits]) != {'0'}:
                dec_part = dec_part[:ndigits]
            else:
                for i, char in enumerate(dec_part):
                    if char != '0':
                        dec_part = dec_part[:i+1]
                        break
        if not dec_part:
            # should happen only if the input is 0.0
            return '0'
        else:
            return '{}.{}'.format(0, dec_part)

似乎适用于问题中提供的输入:

In [81]: inputs = [11111.1, 111.11, 11.111, 1.1111, 0.00111, 0.000011]

In [82]: for inp in inputs:
    ...:     print('Input = {} -- output = {}'.format(inp, format_float(inp, 4)))
Input = 11111.1 -- output = 11111
Input = 111.11 -- output = 111.1
Input = 11.111 -- output = 11.11
Input = 1.1111 -- output = 1.111
Input = 0.00111 -- output = 0.0011
Input = 1.1e-05 -- output = 0.00001

这不会处理负数,但检查它们并使用绝对值非常容易:

def format_float(value, ndigits):
    sign = ''
    if value < 0:
        value = abs(value)
        sign = '-'

    if value >= 1:
        j = min(i-1 for i in range(ndigits+1) if value >= 10 ** (ndigits - i))
        return '{}{:.0{num_digits}f}'.format(sign, value, num_digits=max(0,j))
    s = '{:.17f}'.format(value).rstrip('0')
    _, dec_part = s.split('.')
    if not dec_part:
        # Happens only with 0.0
        return '0'
    if len(dec_part) < ndigits or set(dec_part[:ndigits]) != {'0'}:
        # truncate the decimal representation
        dec_part = dec_part.ljust(ndigits, '0')[:ndigits]
    elif len(dec_part) > ndigits:
        # too small. Just find the first decimal place
        for i, char in enumerate(dec_part):
            if char != '0':
                dec_part = dec_part[:i+1]
                break
    return '{}{}.{}'.format(sign, 0, dec_part)

答案 1 :(得分:0)

在对不同列表进行排序之前,您需要获得十进制级别

import math

def get_decimal_level(number):
    return int(round(math.log(number, 10), 10)) + 1

测试

>>> get_decimal_level(99999)
5
>>> get_decimal_level(100000)
6

让它发挥作用

from collections import defaultdict

def group_list_on_levels(numbers):
    group_numbers = defaultdict(list)

    for number in numbers:
        group_numbers[get_decimal_level(number)].append(number)

    levels = group_numbers.keys()
    levels.sort(reverse=True)

    return [group_numbers[key] for level in levels]

numbers = [31243,543543.654,543543,0.001]
final_list = group_list_on_levels(numbers)
print(final_list) # Check your answer

在控制台中我得到了

[[543543.654, 543543], [31243], [0.001]]

P.S。并且没有十进制字符串转换

答案 2 :(得分:-1)

看不到简单的一两行解决方案。下面的代码解决了您的问题。

def formatNumber(a, DIGITS_TO_KEEP):
    p = a.index('.')
    if p >= DIGITS_TO_KEEP:
        return a[:p]
    else:
        q = 0
        while q+1 < len(a) and (a[q]=='.' or a[q]=='0'):
            q += 1
        return a[:max(DIGITS_TO_KEEP, q)+1]

for case in ['11111.1', '111.11', '11.111', '1.1111', '0.00111', '0.000011']:
    print case, ":", formatNumber(case, 4)