La formattazione galleggia senza zeri finali


181

Come posso formattare un float in modo che non contenga zeri finali? In altre parole, voglio che la stringa risultante sia la più corta possibile.

Per esempio:

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

Quell'esempio non ha alcun senso. 3.14 == 3.140- Sono lo stesso numero in virgola mobile. Del resto 3.140000 è lo stesso numero in virgola mobile. Lo zero non esiste in primo luogo.
S.Lott

33
@ S.Lott - Penso che il problema sia STAMPARE il numero float senza gli zeri finali, non l'equivalenza effettiva di due numeri.
pokstad

1
@pokstad: in tal caso, non c'è zero "superfluo". %0.2fe %0.3fsono i due formati richiesti per produrre gli ultimi numeri a sinistra. Utilizzare %0.2fper produrre gli ultimi due numeri a destra.
S.Lott

6
3.0 -> "3"è ancora un caso d'uso valido. print( '{:,g}'.format( X )ha funzionato per me per produrre 3dove X = 6 / 2e quando X = 5 / 2ho ottenuto un output 2.5come previsto.
ShoeMaker,

1
vecchia domanda, ma ... print("%s"%3.140)ti dà quello che vuoi. (Ho aggiunto una risposta in basso ...)
drevicko,

Risposte:


178

Io, lo farei ('%f' % x).rstrip('0').rstrip('.')- garantisce la formattazione in virgola fissa piuttosto che la notazione scientifica, ecc. Ecc. Sì, non così lucido ed elegante come %g, ma funziona (e non so come forzare %ga non usare mai la notazione scientifica; -).


6
L'unico problema è che '%.2f' % -0.0001ti lascerà -0.00e alla fine -0.
Kos,

3
@ alexanderlukanin13 perché la precisione predefinita è 6, consultare docs.python.org/2/library/string.html : dovresti'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6. usare '% 0.7f' nella soluzione sopra.
derenio,

3
@derenio Un buon punto :-) Posso solo aggiungere che aumentare la precisione sopra '%0.15f'è una cattiva idea, perché iniziano a succedere cose strane .
alexanderlukanin13

1
Nel caso in cui ti trovi nel mezzo di un'altra stringa: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
tollerante ai guasti il

1
@Alex Martelli, sbarazzati del doppio rstripcomando. Usa semplicemente questo invece per rimuovere sia il punto ( .) che tutti gli zeri finali in una sola operazione:('%f' % x).rstrip('.0')
Gabriel Staples,

143

È possibile utilizzare %gper raggiungere questo obiettivo:

'%g'%(3.140)

oppure, per Python 2.6 o superiore:

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

Dai documenti performat : gcause (tra le altre cose)

zeri finali insignificanti [da] rimuovere dal significato e anche il punto decimale viene rimosso se non ci sono cifre rimanenti che lo seguono.


28
Oh, quasi! A volte formatta il float in notazione scientifica ("2.342E + 09") - è possibile disattivarlo, ovvero mostrare sempre tutte le cifre significative?
TarGz,

5
Perché usare '{0:...}'.format(value)quando potresti usare format(value, '...')? Ciò evita di dover analizzare l'identificatore di formato da una stringa di modello altrimenti vuota.
Martijn Pieters

2
@MartijnPieters: il costo minimo di analisi dell'identificatore di formato è sommerso da altri costi generali AFAICT; in pratica, i miei benchmark locali su 3.6 (con l'ambito delle funzioni del microbenchmark per modellare accuratamente il codice reale) hanno format(v, '2.5f')impiegato circa il 10% in più rispetto a '{:2.5f}'.format(v). Anche se non lo fosse, tendo a utilizzare il strmodulo del metodo perché quando devo modificarlo, aggiungere ulteriori valori ad esso, ecc., C'è meno da cambiare. Ovviamente, a partire da 3.6 abbiamo f-string per la maggior parte degli scopi. :-)
ShadowRanger

5
In Python 3.6, questo può essere abbreviato f"{var:g}"dove si vartrova una variabile float.
TheGreatCabbage,

12

Che ne dici di provare l'approccio più semplice e probabilmente più efficace? Il metodo normalize () rimuove tutti gli zeri finali più a destra.

from decimal import Decimal

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

Funziona in Python 2 e Python 3 .

- Aggiornato -

L'unico problema, come ha sottolineato @ BobStein-VisiBone, è che numeri come 10, 100, 1000 ... verranno visualizzati in rappresentazione esponenziale. Questo può essere facilmente risolto utilizzando invece la seguente funzione:

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

2
Tranne che Decimal('10.0').normalize()diventa'1E+1'
Bob Stein,

11

Dopo aver esaminato le risposte a diverse domande simili, questa sembra essere la soluzione migliore per me:

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

Il mio ragionamento:

%g non si libera della notazione scientifica.

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

15 decimali sembrano evitare comportamenti strani e hanno molta precisione per le mie esigenze.

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

Avrei potuto usare format(inputValue, '.15f').invece di '%.15f' % inputValue, ma è un po 'più lento (~ 30%).

Avrei potuto usare Decimal(inputValue).normalize(), ma questo ha anche alcuni problemi. Per uno, è MOLTO più lento (~ 11x). Ho anche scoperto che sebbene abbia una precisione abbastanza grande, soffre comunque di una perdita di precisione durante l'utilizzo normalize().

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

Ancora più importante, mi convertirei ancora Decimalda un floatche può farti finire con qualcosa di diverso dal numero che hai inserito. Penso che Decimalfunzioni meglio quando l'aritmetica rimane Decimale Decimalviene inizializzata con una stringa.

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

Sono sicuro che il problema della precisione di Decimal.normalize()può essere adattato a ciò che è necessario utilizzando le impostazioni di contesto, ma considerando la già bassa velocità e non avendo bisogno di una precisione ridicola e il fatto che mi sarei comunque convertito da un galleggiante e perdere comunque la precisione, non l'ho fatto penso che valga la pena perseguire.

Non mi preoccupo del possibile risultato "-0" poiché -0,0 è un numero in virgola mobile valido e probabilmente sarebbe comunque un evento raro, ma poiché hai menzionato che vuoi mantenere il risultato della stringa il più breve possibile, tu potrebbe sempre utilizzare un ulteriore condizionale a costi di velocità extra molto ridotti.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result

1
Sfortunatamente funziona solo con numeri con meno di approssimativamente) cinque o più cifre a sinistra del decimale. floatToString(12345.6)ritorna '12345.600000000000364'per esempio. Diminuendo il 15 in %.15fa un numero più basso si risolve in questo esempio, ma quel valore deve essere ridotto sempre di più man mano che il numero aumenta. Potrebbe essere calcolato dinamicamente in base al log-base-10 del numero, ma ciò diventa rapidamente molto complicato.
JohnSpeeks

1
Un modo per risolvere quel problema potrebbe essere quello di limitare la lunghezza dell'intero numero (piuttosto che solo le cifre dopo il decimale):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith

@JohnSpeeks Non sono sicuro che questo sia evitabile. È un effetto collaterale di numeri fluttuanti che non sono in grado di rappresentare l'accuratezza se sono necessarie più cifre sul lato sinistro. Da quello che posso dire, il numero che esce come una stringa è lo stesso numero che va come un float, o almeno la rappresentazione più vicina di esso. >>>12345.600000000000364 == 12345.6 True
PolyMesh

Ho scritto un'altra soluzione .
Niitsuma,

8

Ecco una soluzione che ha funzionato per me. È una miscela della soluzione di PolyMesh e l'uso della nuova .format() sintassi .

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

Uscita :

3
3
3
3.1
3.14
3.14

L'unica cosa che non va in questo è che devi impostare un numero ragionevole di cifre decimali. Più alto lo imposti, più numeri precisi puoi rappresentare, ma se lo fai molto, può peggiorare le prestazioni.
beruic,

1
Aggiungendo al commento di beruic, questo non funziona con float di maggiore precisione (ad es. 3.141) Dato che .2fè hard-coded.
TrebledJ

3

Puoi semplicemente usare format () per raggiungere questo obiettivo:

format(3.140, '.10g') dove 10 è la precisione che desideri.


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

Non intendi int(a) if a % 1 else a?
beruic,

Caro Beruic, la tua risposta risulta negativa. a if a % 1 else int(a)è corretto. La domanda necessita dell'output in stringa, quindi ho appena aggiuntostr
Shameem,

Ah, ho capito adesso. a % 1è vero perché è diverso da zero. L'ho percepito implicitamente e erroneamente come a % 1 == 0.
beruic,

2

Mentre la formattazione è probabilmente la maggior parte del modo Pythonic, ecco una soluzione alternativa che utilizza lo more_itertools.rstripstrumento.

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"

Il numero viene convertito in una stringa, che è spogliata di caratteri finali che soddisfano un predicato. La definizione della funzionefmt non è richiesta, ma viene utilizzata qui per testare le asserzioni, che passano tutte. Nota: funziona su input di stringa e accetta predicati opzionali.

Vedi anche i dettagli su questa libreria di terze parti, more_itertools.


1

Se riesci a vivere con 3. e 3.0 che appaiono come "3.0", un approccio molto semplice che sposta a destra gli zeri dalle rappresentazioni float:

print("%s"%3.140)

(grazie @ellimilial per aver sottolineato le eccezioni)


1
Ma lo print("%s"%3.0)fa.
ellimilial

1

L'uso del pacchetto QuantiPhy è un'opzione. Normalmente QuantiPhy viene utilizzato quando si lavora con numeri con unità e fattori di scala SI, ma ha una varietà di opzioni per la formattazione dei numeri.

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

E non utilizzerà la notazione elettronica in questa situazione:

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

Un'alternativa che potresti preferire è usare i fattori di scala SI, forse con le unità.

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

0

OP vorrebbe rimuovere gli zeri superflui e rendere la stringa risultante il più corta possibile.

Trovo che la formattazione esponenziale% g accorcia la stringa risultante per valori molto grandi e molto piccoli. Il problema si presenta con valori che non necessitano di notazione esponenziale, come 128.0, che non è né molto grande né molto piccolo.

Ecco un modo per formattare i numeri come stringhe brevi che utilizza la notazione esponenziale% g solo quando Decimal.normalize crea stringhe troppo lunghe. Questa potrebbe non essere la soluzione più veloce (poiché utilizza 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

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

Lo uso per formattare i float per tracciare gli zeri.


3143.93 -> 3.1e + 03
j35t3r

0

Ecco la risposta:

import numpy

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

uscita "3.14" e "3"

trim='-' rimuove sia gli zero finali sia i decimali.


-1

Usa% g con una larghezza sufficiente, ad esempio '% .99g'. Stamperà in notazione a virgola fissa per qualsiasi numero ragionevolmente grande.

EDIT: non funziona

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'

1
.99è precisione, non larghezza; un po 'utile ma non puoi impostare la precisione effettiva in questo modo (oltre a troncarla tu stesso).
Kos,

-1

Puoi usare max()così:

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


1
devi considerare il caso in cui xè negativo. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix,

Un metodo utile quando voglio fare json stringify. float 1.0 cambia in int 1, quindi funziona esattamente come in javascript.
pingze,

-3

Puoi raggiungerlo nel modo più pitonico del genere:

python3:

"{:0.0f}".format(num)

Hai ragione. Il modo più semplice è usare "{: g}". Format (num)
Pyglouthon

-4

Gestisci% f e dovresti metterlo

% .2f

, dove: .2f == .00 float.

Esempio:

stampa "Prezzo:% .2f"% prezzi [prodotto]

produzione:

Prezzo: 1,50


1
Questo è chiaramente indicato non quello che la domanda vuole. È necessario effettuare il downgrade.
episodeyang
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.