Formatage des flottants sans zéros de fin

188

Comment puis-je formater un flottant pour qu'il ne contienne pas de zéros de fin? En d'autres termes, je veux que la chaîne résultante soit aussi courte que possible.

Par exemple:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
TarGz
la source
1
Cet exemple n'a aucun sens. 3.14 == 3.140- C'est le même nombre à virgule flottante. D'ailleurs, 3,140000 est le même nombre à virgule flottante. Le zéro n'existe pas en premier lieu.
S.Lott
39
@ S.Lott - Je pense que le problème est d'IMPRIMER le nombre flottant sans les zéros de fin, pas l'équivalence réelle de deux nombres.
pokstad
1
@pokstad: Dans ce cas, il n'y a pas de zéro "superflu". %0.2fet %0.3fsont les deux formats requis pour produire les derniers nombres sur la gauche. Utilisez %0.2fpour produire les deux derniers nombres sur la droite.
S.Lott
6
3.0 -> "3"est toujours un cas d'utilisation valide. print( '{:,g}'.format( X )travaillé pour moi pour sortir 3X = 6 / 2et quand X = 5 / 2j'ai obtenu une sortie de 2.5comme prévu.
ShoeMaker
1
vieille question, mais .. print("%s"%3.140)vous donne ce que vous voulez. (J'ai ajouté une réponse ci-dessous ...)
drevicko

Réponses:

187

Moi, je le ferais ('%f' % x).rstrip('0').rstrip('.')- garantit le formatage en virgule fixe plutôt que la notation scientifique, etc. etc. Oui, pas aussi lisse et élégant que %g, mais ça marche (et je ne sais pas comment forcer %gà ne jamais utiliser la notation scientifique; -).

Alex Martelli
la source
6
Le seul problème avec cela est de '%.2f' % -0.0001vous laisser avec -0.00et finalement -0.
Kos
3
@ alexanderlukanin13 car la précision par défaut est 6, voir docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.Vous devrez utiliser '% 0.7f' dans la solution ci-dessus.
derenio
3
@derenio Bon point :-) Je peux seulement ajouter qu'élever la précision ci '%0.15f'- dessus est une mauvaise idée, car des choses étranges commencent à se produire.
alexanderlukanin13
2
Au cas où vous seriez au milieu d'une autre chaîne: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
tolérant aux pannes
2
@Peter Schorn: vous avez raison, l'optimisation de Gabriel Staples est mauvaise, mais c'est parce que l'astuce de l'OP vous oblige à supprimer tous les zéros PUIS toutes les décimales et puis PAS PLUS DE ZÉROS. L'approche de Gabriel supprime simplement tous les zéros et les points jusqu'à ce qu'il atteigne autre chose.
Scott Stafford
151

Vous pouvez utiliser %gpour y parvenir:

'%g'%(3.140)

ou, pour Python 2.6 ou supérieur:

'{0:g}'.format(3.140)

À partir de la documentation pourformat : gcauses (entre autres)

des zéros de fin insignifiants [à supprimer] du significand, et le point décimal est également supprimé s'il n'y a pas de chiffres restants le suivant.

unutbu
la source
32
Oh presque! Parfois, il formate le flottant en notation scientifique ("2.342E + 09") - est-il possible de le désactiver, c'est-à-dire toujours afficher tous les chiffres significatifs?
TarGz
5
Pourquoi utiliser '{0:...}'.format(value)quand vous pourriez utiliser format(value, '...')? Cela évite d'avoir à analyser le spécificateur de format à partir d'une chaîne de modèle qui est autrement vide.
Martijn Pieters
2
@MartijnPieters: Le coût minuscule de l'analyse du spécificateur de format est submergé par d'autres frais généraux AFAICT; en pratique, mes benchmarks locaux sur 3.6 (avec la portée des fonctions du microbenchmark pour modéliser avec précision le code réel) ont format(v, '2.5f')pris environ 10% de plus que '{:2.5f}'.format(v). Même si ce n'est pas le cas, j'ai tendance à utiliser le strformulaire de méthode car lorsque j'ai besoin de le modifier, d'y ajouter des valeurs supplémentaires, etc., il y a moins à changer. Bien sûr, à partir de 3.6, nous avons des chaînes f pour la plupart des cas. :-)
ShadowRanger
5
Dans Python 3.6, cela peut être raccourci à f"{var:g}"varest une variable flottante.
TheGreatCabbage
15

Après avoir examiné les réponses à plusieurs questions similaires, cela semble être la meilleure solution pour moi:

def floatToString(inputValue):
    return ('%.15f' % inputValue).rstrip('0').rstrip('.')

Mon raisonnement:

%g ne se débarrasse pas de la notation scientifique.

>>> '%g' % 0.000035
'3.5e-05'

15 décimales semblent éviter les comportements étranges et ont beaucoup de précision pour mes besoins.

>>> ('%.15f' % 1.35).rstrip('0').rstrip('.')
'1.35'
>>> ('%.16f' % 1.35).rstrip('0').rstrip('.')
'1.3500000000000001'

J'aurais pu utiliser à la format(inputValue, '.15f').place de '%.15f' % inputValue, mais c'est un peu plus lent (~ 30%).

J'aurais pu l'utiliser Decimal(inputValue).normalize(), mais cela pose également quelques problèmes. D'une part, c'est BEAUCOUP plus lent (~ 11x). J'ai également trouvé que même s'il a une assez grande précision, il souffre toujours d'une perte de précision lors de l'utilisation normalize().

>>> Decimal('0.21000000000000000000000000006').normalize()
Decimal('0.2100000000000000000000000001')
>>> Decimal('0.21000000000000000000000000006')
Decimal('0.21000000000000000000000000006')

Plus important encore, je serais toujours en train de convertir à Decimalpartir d'un floatqui peut vous faire finir avec autre chose que le nombre que vous avez mis là-dedans. Je pense que cela Decimalfonctionne mieux lorsque l'arithmétique reste Decimalet que le Decimalest initialisé avec une chaîne.

>>> Decimal(1.35)
Decimal('1.350000000000000088817841970012523233890533447265625')
>>> Decimal('1.35')
Decimal('1.35')

Je suis sûr que le problème de précision de Decimal.normalize()peut être ajusté à ce qui est nécessaire en utilisant les paramètres de contexte, mais compte tenu de la vitesse déjà lente et de ne pas avoir besoin d'une précision ridicule et du fait que je serais toujours en train de convertir un flotteur et de perdre de la précision de toute façon, je n'ai Je ne pense pas que cela valait la peine d'être poursuivi.

Je ne suis pas préoccupé par le résultat possible "-0" car -0.0 est un nombre à virgule flottante valide et ce serait probablement une occurrence rare de toute façon, mais comme vous avez mentionné que vous souhaitez garder le résultat de la chaîne aussi court que possible, vous pourrait toujours utiliser une condition supplémentaire à très peu de frais de vitesse supplémentaire.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
PolyMesh
la source
1
Malheureusement, ne fonctionne qu'avec des nombres avec moins de cinq chiffres ou plus à gauche de la décimale. floatToString(12345.6)renvoie '12345.600000000000364'par exemple. Réduire le 15 %.15fà un nombre inférieur le résout dans cet exemple, mais cette valeur doit être diminuée de plus en plus à mesure que le nombre augmente. Il pourrait être calculé dynamiquement sur la base du log-base-10 du nombre, mais cela devient rapidement très compliqué.
JohnSpeeks
1
Une façon de résoudre ce problème pourrait être de limiter la longueur du nombre entier (plutôt que juste les chiffres après la décimale):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith
@JohnSpeeks Je ne suis pas sûr que cela soit évitable. C'est un effet secondaire du fait que les nombres flottants ne peuvent pas représenter la précision si plus de chiffres sont nécessaires sur le côté gauche. D'après ce que je peux dire, le nombre qui apparaît sous forme de chaîne est le même nombre que celui qui entre en tant que flottant, ou du moins la représentation la plus proche de celui-ci. >>>12345.600000000000364 == 12345.6 True
PolyMesh
J'ai écrit une autre solution .
niitsuma
12

Pourquoi ne pas essayer l'approche la plus simple et probablement la plus efficace? La méthode normalize () supprime tous les zéros les plus à droite.

from decimal import Decimal

print (Decimal('0.001000').normalize())
# Result: 0.001

Travaille dans Python 2 et Python 3 .

-- Mis à jour --

Le seul problème, comme l'a souligné @ BobStein-VisiBone, est que des nombres comme 10, 100, 1000 ... seront affichés en représentation exponentielle. Cela peut être facilement corrigé en utilisant la fonction suivante à la place:

from decimal import Decimal


def format_float(f):
    d = Decimal(str(f));
    return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
Ander
la source
2
Sauf Decimal('10.0').normalize()devient'1E+1'
Bob Stein
9

Voici une solution qui a fonctionné pour moi. C'est un mélange de la solution de PolyMesh et de l'utilisation de la nouvelle .format() syntaxe .

for num in 3, 3., 3.0, 3.1, 3.14, 3.140:
    print('{0:.2f}'.format(num).rstrip('0').rstrip('.'))

Sortie :

3
3
3
3.1
3.14
3.14
Kaushal Modi
la source
Le seul problème avec celui-ci est que vous devez définir un nombre raisonnable de chiffres décimaux. Plus vous le définissez, plus vous pouvez représenter des nombres précis, mais si vous le faites souvent, cela peut dégrader les performances.
beruic le
1
Ajoutant au commentaire de beruic, cela ne fonctionne pas pour les flotteurs de plus grande précision (par exemple 3.141) car le .2fest codé en dur.
TrebledJ
4

Vous pouvez simplement utiliser format () pour y parvenir:

format(3.140, '.10g') où 10 est la précision souhaitée.

clel
la source
3

Bien que le formatage soit probablement le moyen le plus pythonique, voici une solution alternative utilisant l' more_itertools.rstripoutil.

import more_itertools as mit


def fmt(num, pred=None):
    iterable = str(num)
    predicate = pred if pred is not None else lambda x: x in {".", "0"}
    return "".join(mit.rstrip(iterable, predicate))

assert fmt(3) == "3"
assert fmt(3.) == "3"
assert fmt(3.0) == "3"
assert fmt(3.1) == "3.1"
assert fmt(3.14) == "3.14"
assert fmt(3.140) == "3.14"
assert fmt(3.14000) == "3.14"
assert fmt("3,0", pred=lambda x: x in set(",0")) == "3"

Le nombre est converti en une chaîne, qui est débarrassée des caractères de fin qui satisfont un prédicat. La définition de la fonction fmtn'est pas obligatoire, mais elle est utilisée ici pour tester les assertions, qui réussissent toutes. Remarque: il fonctionne sur les entrées de chaîne et accepte les prédicats optionnels.

Voir aussi les détails sur cette bibliothèque tierce, more_itertools.

pylang
la source
2
>>> str(a if a % 1 else int(a))
Honte
la source
Tu ne veux pas dire int(a) if a % 1 else a?
beruic le
Cher Beruic, votre réponse aboutit à une réponse négative. a if a % 1 else int(a)est correct. La question nécessite une sortie en chaîne, je viens donc d'ajouterstr
Shameem
Ah, je comprends maintenant. a % 1est la vérité parce qu'elle est non nulle. Je l'ai implicitement et à tort perçu comme a % 1 == 0.
beruic
1

L'utilisation du package QuantiPhy est une option. Normalement, QuantiPhy est utilisé lorsque vous travaillez avec des nombres avec des unités et des facteurs d'échelle SI, mais il dispose d'une variété d'options de formatage de nombres intéressantes.

    >>> from quantiphy import Quantity

    >>> cases = '3 3. 3.0 3.1 3.14 3.140 3.14000'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:p}')
          3 -> 3
         3. -> 3
        3.0 -> 3
        3.1 -> 3.1
       3.14 -> 3.14
      3.140 -> 3.14
    3.14000 -> 3.14

Et il n'utilisera pas la notation électronique dans cette situation:

    >>> cases = '3.14e-9 3.14 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case)
    ...    print(f'{case:>7} -> {q:,p}')
    3.14e-9 -> 0
       3.14 -> 3.14
     3.14e9 -> 3,140,000,000

Une alternative que vous pourriez préférer est d'utiliser des facteurs d'échelle SI, peut-être avec des unités.

    >>> cases = '3e-9 3.14e-9 3 3.14 3e9 3.14e9'.split()
    >>> for case in cases:
    ...    q = Quantity(case, 'm')
    ...    print(f'{case:>7} -> {q}')
       3e-9 -> 3 nm
    3.14e-9 -> 3.14 nm
          3 -> 3 m
       3.14 -> 3.14 m
        3e9 -> 3 Gm
     3.14e9 -> 3.14 Gm
Automne McClellan
la source
0

OP souhaite supprimer les zéros superflus et rendre la chaîne résultante aussi courte que possible.

Je trouve que le formatage exponentiel% g raccourcit la chaîne résultante pour les valeurs très grandes et très petites. Le problème vient des valeurs qui n'ont pas besoin de notation exponentielle, comme 128.0, qui n'est ni très grande ni très petite.

Voici une façon de formater les nombres sous forme de chaînes courtes qui utilise la notation exponentielle% g uniquement lorsque Decimal.normalize crée des chaînes trop longues. Ce n'est peut-être pas la solution la plus rapide (car elle utilise Decimal.normalize)

def floatToString (inputValue, precision = 3):
    rc = str(Decimal(inputValue).normalize())
    if 'E' in rc or len(rc) > 5:
        rc = '{0:.{1}g}'.format(inputValue, precision)        
    return rc

inputs = [128.0, 32768.0, 65536, 65536 * 2, 31.5, 1.000, 10.0]

outputs = [floatToString(i) for i in inputs]

print(outputs)

# ['128', '32768', '65536', '1.31e+05', '31.5', '1', '10']
kinok
la source
0

Pour le flotteur, vous pouvez utiliser ceci:

def format_float(num):
    return ('%i' if num == int(num) else '%s') % num

Essaye-le:

>>> format_float(1.00000)
'1'
>>> format_float(1.1234567890000000000)
'1.123456789'

Pour décimal, voir la solution ici: https://stackoverflow.com/a/42668598/5917543

Artem Skoretskiy
la source
0

Si vous pouvez vivre avec 3. et 3.0 apparaissant comme "3.0", une approche très simple qui supprime à droite les zéros des représentations flottantes:

print("%s"%3.140)

(merci @ellimilial pour avoir signalé les exceptions)

Drevicko
la source
1
Mais le print("%s"%3.0)fait.
ellimilial
0

"{:.5g}".format(x)

J'utilise ceci pour formater les flottants pour suivre les zéros.

martin sun
la source
3143.93 -> 3.1e + 03
j35t3r
0

Voici la réponse:

import numpy

num1 = 3.1400
num2 = 3.000
numpy.format_float_positional(num1, 3, trim='-')
numpy.format_float_positional(num2, 3, trim='-')

sortie "3.14" et "3"

trim='-' supprime à la fois les zéros de fin et la décimale.

comport9
la source
-1

Utilisez% g avec une largeur suffisamment grande, par exemple '% .99g'. Il imprimera en notation à virgule fixe pour tout nombre raisonnablement grand.

EDIT: ça ne marche pas

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'
alexanderlukanin13
la source
1
.99est la précision, pas la largeur; un peu utile, mais vous ne pouvez pas définir la précision réelle de cette façon (à part la tronquer vous-même).
Kos
-1

Vous pouvez utiliser max()comme ceci:

print(max(int(x), x))

elig
la source
1
vous devez considérer le cas où xest négatif. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix
Une méthode utile lorsque je veux faire json stringify. float 1.0 passe à int 1, donc il fonctionne exactement comme en javascript.
pingze
-3

Vous pouvez y parvenir de la manière la plus pythonique comme ça:

python3:

"{:0.0f}".format(num)
Lan Vukušič
la source
Tu as raison. Le moyen le plus simple est d'utiliser "{: g}". Format (num)
Pyglouthon
-4

Gestion de% f et vous devriez mettre

% .2f

, où: .2f == .00 flotte.

Exemple:

print "Prix:% .2f"% prix [produit]

production:

Prix: 1,50

Alex M.
la source
2
Ce n'est clairement pas ce que veut la question. Besoin de voter contre.
episodeyang