web-dev-qa-db-fra.com

Mise en forme des flotteurs en Python sans zéros superflus

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

Comme:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
132
TarGz

('%f' % x).rstrip('0').rstrip('.') - garantit un formatage à virgule fixe plutôt que la notation scientifique, etc. Oui, pas aussi élégant et élégant que %g, mais cela fonctionne (et je ne sais pas forcer %g à ne jamais utiliser notation scientifique;-).

151
Alex Martelli

Vous pouvez utiliser %g pour y parvenir:

'%g'%(3.140)

ou, pour Python 2.6 ou supérieur:

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

À partir des docs pour format : g causes (entre autres)

zéros à la fin non significatifs [à être] retiré de la signification, et le la virgule décimale est également supprimée s'il y a il n'y a pas de chiffres restants à la suite.

121
unutbu

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 supprime pas 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 format(inputValue, '.15f'). au lieu de '%.15f' % inputValue, mais c'est un peu plus lent (~ 30%).

J'aurais pu utiliser Decimal(inputValue).normalize(), mais cela pose également quelques problèmes. D'une part, il est beaucoup plus lent (~ 11x). J'ai également constaté que bien qu'il ait une assez grande précision, il souffre toujours d'une perte de précision lors de l'utilisation de normalize().

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

Plus important encore, je serais toujours en train de convertir Decimal à partir de float, ce qui peut vous amener à obtenir autre chose que le nombre que vous avez indiqué. Je pense que Decimal fonctionne mieux lorsque l'arithmétique reste dans Decimal et que la Decimal est initialisée 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 à l’aide des paramètres de contexte, mais compte tenu de la vitesse déjà lente et du fait que je n’ai toujours pas besoin d’une précision ridicule et que je serais toujours en train de convertir un flottant et de perdre de la précision, ne pensais pas que cela valait la peine d'être poursuivi.

Je ne m'inquiète pas du résultat "-0" possible, car -0,0 est un nombre à virgule flottante valide. Ce serait probablement un cas rare de toute façon, mais puisque vous avez mentionné que vous souhaitiez que le résultat de la chaîne soit 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
10
PolyMesh

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

from decimal import Decimal

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

Fonctionne dans Python 2 et Python 3 .

-- Mis à jour --

Le seul problème mentionné par @BobStein-VisiBone est que des nombres tels que 10, 100, 1000 ... seront affichés sous forme de représentation exponentielle. Cela peut être facilement résolu en utilisant la fonction suivante:

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()
9
Ander

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
4
Kaushal Modi

Bien que le formatage corresponde probablement à la plupart des méthodes Pythonic, voici une solution alternative utilisant l’outil more_itertools.rstrip .

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 respectant un prédicat. La définition de fonction fmt n'est pas obligatoire, mais elle est utilisée ici pour tester les assertions, qui passent toutes bien. Remarque: cela fonctionne sur les entrées de chaîne et accepte les prédicats facultatifs.

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

2
pylang

Vous pouvez simplement utiliser format () pour y parvenir:

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

2
clel
>>> str(a if a % 1 else int(a))
1
Shameem

Si vous pouvez vivre avec 3. et 3.0 en tant que "3.0", une approche très simple qui supprime les zéros des représentations flottantes

print("%s"%3.140)

(merci @ellimilial pour avoir signalé les exceptions)

1
drevicko

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 un moyen de formater des nombres en tant que chaînes courtes utilisant la notation exponentielle% g uniquement lorsque Decimal.normalize crée des chaînes trop longues. Cela peut ne pas être la solution la plus rapide (car il 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']
0
kinok

Pour float, 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 Decimal, voir la solution ici: https://stackoverflow.com/a/42668598/5917543

0
Artem Skoretskiy

Manipulation% f et vous devriez mettre 

% .2f

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

Exemple:

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

sortie:

Prix: 1.50

0
Alex M.