Python - 也适用于小数的千位分隔符,保留 4 位有效数字

Python - Thousands separator that also works for decimals, keeping 4 significant digits

我正在使用 API 来获取有关加密货币的数据 (https://api.coinmarketcap.com/v1/ticker/?convert=USD),正如你们中的一些人可能知道的那样,加密货币的价值可能超过 4000 美元 (BTC) 或低于 0.000001 美元其他。我必须工作并轻松比较所有这些数字。

在加拿大,我们使用 space 字符作为千位分隔符。我希望它能为数千人工作,但也能对小数位进行分组。另外,我需要一种方法来只保留一些 significant 数字。例如,对于比特币,我不需要小数部分。我想总是至少有 4 有效数字,这基本上意味着,如果 number >= 1000,我不需要小数部分(但我保留完整的整数部分),如果它是 < 0,我希望在第一个非零数字之后至少有 3 个附加数字(必要时四舍五入)。

我想要的输入 => 输出示例:

65410845186.1      => 65 410 845 186
43245.1            => 43 245
285.1234           => 285.1
0.01234567         => 0.123 5
0.001234054        => 0.001 234
0.001034538        => 0.001 035
0.00010001         => 0.000 100 0
1                  => 1.000
1.0006             => 1.001

脚本需要在不同的操作系统上运行。我不认为任何 locale 或微不足道的 format 实现都可以在这里工作。什么是双向添加 space 分隔符的最佳方法,使用点作为小数点分隔符,同时保留 4 个有效数字(必要时四舍五入)除了数字 >= 1000 我只保留整数部分?

我从我在 Whosebug 上找到的另一段代码中得到了 space 分隔符作为千位分隔符:

def splitThousands(s, tSep=' ', dSep=','):
    if s.rfind('.')>0:
        rhs=s[s.rfind('.')+1:]
        s=s[:s.rfind('.')]
        if len(s) <= 3: return s + dSep + rhs
        return splitThousands(s[:-3], tSep) + tSep + s[-3:] + dSep + rhs
    else:
        if len(s) <= 3: return s
        return splitThousands(s[:-3], tSep) + tSep + s[-3:]

但是它没有考虑有效位数,也没有为小数部分添加分隔符。

注意:这些值波动很多,我确实真的介意浮点值[=18]的极端极端情况=] 将四舍五入为 1.001.

不完全是一个干净或直接的解决方案,但这里有一个符合要求的函数:

from math import floor, log10

def format_btc(value, tSep=' ', dSep='.', precision=4, grouping=3):
    int_digits = max(int(floor(log10(value))) + 1, 0)
    if int_digits >= precision:
        int_part = int(round(value))
        decimal_part = None
    else:
        int_part = int(floor(value))
        decimal_part = value % 1.0
    int_str = str(int_part)[::-1]
    int_str = [int_str[i:i + grouping][::-1]
               for i in range(0, len(int_str), grouping)]
    int_str = tSep.join(reversed(int_str))
    if decimal_part is None:
        return int_str
    if int_digits > 0:
        num_decimals = precision - int_digits
    else:
        decimal_str = "{:.{precision}e}".format(decimal_part, precision=precision - 1)
        decimal_pos = -int(decimal_str.split("e")[1])
        num_decimals = decimal_pos + precision - 1
    decimal_str = "{:.{precision}f}".format(decimal_part, precision=num_decimals)
    decimal_str = decimal_str.split(".")[1]
    if int_digits > 0:
        decimal_str = decimal_str[:precision - int_digits]
    decimal_str = [decimal_str[i:i + grouping] for i in range(0, len(decimal_str), grouping)]
    decimal_str = tSep.join(decimal_str)
    return dSep.join((int_str, decimal_str))

tests = [65410845186.123456, 4324.1, 285.1234, 0.01234567, 0.001234054,
         0.001034538, 0.00010001, 1390390000.0, 1.0006]

for test in tests:
    print("{:<20f} => {:s}".format(test, format_btc(test)))

输出:

65410845186.123459   => 65 410 845 186
4324.100000          => 4 324
285.123400           => 285.1
0.012346             => 0.012 35
0.001234             => 0.001 234
0.001035             => 0.001 035
0.000100             => 0.000 100 0
1390390000.000000    => 1 390 390 000
1.000600             => 1.001