可变精度的百分比

时间:2009-09-29 20:19:33

标签: python algorithm precision

我想显示三位小数的百分比,除非它大于99%。然后,我想显示包含所有可用9和3个非九个字符的数字。

我怎么能用Python写这个? "%.8f"字符串格式化工作正常,但我需要在最后一串九个字符后保留最后三个字符。

所以:
    54.8213% - > 54.821%
    95.42332% - > 95.423%
    99.9932983% - > 99.99330%
    99.99999999992318 - > 99.9999999999232%

6 个答案:

答案 0 :(得分:3)

试试这个:

import math
def format_percentage(x, precision=3):
    return ("%%.%df%%%%" % (precision - min(0,math.log10(100-x)))) % x

答案 1 :(得分:2)

Mark Ransom的回答是美好的。通过一些工作,它可以解决任何输入的问题。我继续前进并完成了一些工作。

你只需要在nines()中添加一些代码:

def nines(x):
    x = abs(x)  # avoid exception caused if x is negative
    x -= int(x)  # keep fractional part of x only
    cx = ceilpowerof10(x) - x
    if 0 == cx:
        return 0  # if x is a power of 10, it doesn't have a string of 9's!
    return -int(math.log10(cx))

然后threeplaces()适用于任何事情。以下是一些测试用例:

>>> threeplaces(0.9999357)
'0.9999357'
>>> threeplaces(1000.9999357)
'1000.9999357'
>>> threeplaces(-1000.9999357)
'-1000.9999357'
>>> threeplaces(0.9900357)
'0.99004'
>>> threeplaces(1000.9900357)
'1000.99004'
>>> threeplaces(-1000.9900357)
'-1000.99004'

答案 2 :(得分:1)

def ceilpowerof10(x):
    return math.pow(10, math.ceil(math.log10(x)))

def nines(x):
    return -int(math.log10(ceilpowerof10(x) - x))

def threeplaces(x):
    return ('%.' + str(nines(x) + 3) + 'f') % x

请注意,nines()会以10的幂为单位抛出一个错误,需要更多的工作才能使所有输入都安全。负数也可能存在一些问题。

答案 3 :(得分:0)

试试这个:

def print_percent(p):    
    for i in range(30):
        if p <= 100. - 10.**(-i):
            print ("%." + str(max(3,3+i-1)) + "f") % p
            return

或者如果您只想检索字符串

def print_percent(p):    
    for i in range(20):
        if p <= 100. - 10.**(-i):
            return ("%." + str(max(3,3+i-1)) + "f") % p

答案 4 :(得分:0)

我完全相信标准格式化是不可能的。我建议使用类似下面的内容(C#像伪代码)。特别是我建议依赖字符串操作而不使用数学代码,因为有许多可能的精度和舍入问题。

string numberString = number.ToStringWithFullPrecision();

int index = numberString.IndexOf('.');

while ((index < numberString.Length - 1) && (numberString[index + 1] == '9'))
{
    index++;
}

WriteLine(number.PadRightWithThreeZeros().SubString(0, index + 4));

如果您喜欢正则表达式,可以使用它们。采用以下表达式并将其与用三个零填充的完整精度数字符串进行匹配,然后就完成了。

^([0-9]|[1-9][0-9]|100)\.(9*)([0-8][0-9]{2})

我刚才意识到这两个建议都可能导致舍入错误。当99.91238123成为99.9123时,99.9124变为{{1}} - 因此最后的数字需要进一步更正。容易做,但让我的建议更加丑陋。这远非优雅而智能的算法。

答案 5 :(得分:0)

 def ilike9s(f):
   return re.sub(r"(\d*\.9*\d\d\d)\d*",r"\1","%.17f" % f)

因此...

>>> ilike9s(1.0)
'1.000'
>>> ilike9s(12.9999991232132132)
'12.999999123'
>>> ilike9s(12.345678901234)
'12.345'

不要忘记import re