2009-09-29 7 views
0

Je voudrais afficher un pourcentage avec trois décimales, sauf s'il est supérieur à 99%. Ensuite, je voudrais afficher le numéro avec tous les neuf disponibles plus 3 non-neuf caractères. Comment puis-je écrire ceci en Python? La mise en forme des chaînes "%.8f" fonctionne correctement, mais je dois conserver les trois derniers caractères après la dernière chaîne de neuf.Pourcentage avec une précision variable

donc:
54,8213% -> 54,821%
95,42332% -> 95,423%
99,9932983% -> 99,99330%
99,99999999992318 -> 99,9999999999232%

+0

Juste par curiosité, pour quoi l'utilisez-vous? Je connais un domaine dans lequel la convention «neuf» est fréquemment utilisée. Cela devrait faire partie du module mathématique imo. – physicsmichael

+0

Il est utilisé pour afficher le rendement d'un processus industriel qui devrait idéalement avoir des valeurs très élevées (4-5 sigma). –

Répondre

3

Essayez ceci:

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

Beaucoup plus simple que le mien - j'aurais dû remarquer qu'il s'agissait de pourcentages, et le maximum serait toujours 100. –

+0

Mais notez que format_percentage (100) renvoie une erreur. –

+0

La fonction donnée est indéfinie pour les valeurs> = 100, exactement comme l'instruction d'origine. –

0

Essayez ceci:

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

ou si vous voulez simplement récupérer la chaîne

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

(notez que vous pouvez agrandir '30' mais c'est déjà plus que la précision float 64 bits) – astrofrog

0

Je suis assez confiant que ce n'est pas possible avec le formage standard. Je suggère d'utiliser quelque chose comme le suivant (pseudo code C#). Surtout je suggère de compter sur les opérations de chaîne et de ne pas utiliser de code mathématique en raison de nombreux problèmes de précision et d'arrondi possibles.

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)); 

Si vous aimez l'expression régulière, vous pouvez les utiliser pour. Prenez l'expression suivante et faites-la correspondre à la chaîne de nombre de précision complète avec trois zéros et vous avez terminé.

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

Je viens de réaliser que les deux suggestion peut provoquer des erreurs d'arrondi. 99.91238123 devient 99.9123 quand il devrait devenir 99.9124 - donc les derniers chiffres nécessitent une correction supplémentaire. Facile à faire, mais rend ma suggestion encore plus laide. C'est loin d'un algorithme élégant et intelligent.

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 

Notez que nines() renvoie une erreur sur le nombre qui sont une puissance de 10 pour commencer, il faudrait un peu plus de travail pour le rendre sûr pour toutes les entrées. Il y a probablement aussi des problèmes avec des nombres négatifs.

+0

J'ai fait le peu de travail pour le rendre sûr pour toutes les entrées; voir ma réponse ci-dessous. – steveha

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

Alors ...

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

Et ne pas oublier de import re

+0

C'est penser en dehors de la boîte. J'aime ça. :-) – steveha

+1

Un problème: il ne tourne pas correctement. 12.3456789devrait vraiment imprimer comme 12.346. – steveha

+0

Le problème d'arrondi est un problème pour moi. Sinon, j'aimerais utiliser des expressions régulières. –

2

La réponse de Mark Ransom est une belle chose. Avec un peu de travail, il peut résoudre le problème pour toutes les entrées. Je suis allé de l'avant et fait le peu de travail.

Vous avez juste besoin d'ajouter un code à quatre épingles():

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)) 

Ensuite threeplaces() fonctionne pour quoi que ce soit.Voici quelques cas de test:

>>> 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' 
Questions connexes