Die Formatierung schwebt ohne nachgestellte Nullen

180

Wie kann ich einen Float so formatieren, dass er keine nachgestellten Nullen enthält? Mit anderen Worten, ich möchte, dass die resultierende Zeichenfolge so kurz wie möglich ist.

Beispielsweise:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
TarGz
quelle
Dieses Beispiel macht überhaupt keinen Sinn. 3.14 == 3.140- Sie sind die gleiche Gleitkommazahl. Im Übrigen ist 3.140000 dieselbe Gleitkommazahl. Die Null existiert überhaupt nicht.
S.Lott
33
@ S.Lott - Ich denke, das Problem ist das DRUCKEN der Gleitkommazahl ohne die nachgestellten Nullen, nicht die tatsächliche Äquivalenz von zwei Zahlen.
Pokstad
1
@pokstad: In diesem Fall gibt es keine "überflüssige" Null. %0.2fund %0.3fsind die beiden Formate, die erforderlich sind, um die letzten Zahlen auf der linken Seite zu erzeugen. Verwenden Sie %0.2fdiese Option , um die letzten beiden Zahlen rechts zu erzeugen.
S.Lott
6
3.0 -> "3"ist immer noch ein gültiger Anwendungsfall. print( '{:,g}'.format( X )arbeitete für mich, um auszugeben, 3wo X = 6 / 2und wann X = 5 / 2ich eine Ausgabe von 2.5wie erwartet bekam.
ShoeMaker
1
alte frage, aber .. print("%s"%3.140)gibt dir was du willst. (Ich habe unten unten eine Antwort hinzugefügt ...)
drevicko

Antworten:

175

Ich würde es tun ('%f' % x).rstrip('0').rstrip('.')- garantiert eine Festkomma-Formatierung anstelle einer wissenschaftlichen Notation usw. usw. Ja, nicht so schick und elegant wie %g, aber es funktioniert (und ich weiß nicht, wie ich zwingen soll %g, niemals wissenschaftliche Notation zu verwenden; -).

Alex Martelli
quelle
6
Das einzige Problem dabei ist '%.2f' % -0.0001, dass Sie mit -0.00und letztendlich verlassen werden -0.
Kos
3
@ alexanderlukanin13, da die Standardgenauigkeit 6 ist, siehe docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.In der obigen Lösung müssten Sie '% 0.7f' verwenden.
derenio
3
@derenio Guter Punkt :-) Ich kann nur hinzufügen, dass '%0.15f'es eine schlechte Idee ist , die Präzision oben zu erhöhen , weil seltsame Dinge passieren.
Alexanderlukanin13
1
Für den Fall, dass Sie sich in der Mitte einer anderen Zeichenfolge befinden: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
Fehlertolerant
1
@ Alex Martelli, werde den doppelten rstripBefehl los . Verwenden Sie stattdessen einfach dies, um sowohl den Punkt ( .) als auch alle nachfolgenden Nullen anschließend in einer Operation ('%f' % x).rstrip('.0')
Gabriel Staples
143

Sie könnten dies verwenden %g, um dies zu erreichen:

'%g'%(3.140)

oder für Python 2.6 oder besser:

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

Aus den Dokumenten fürformat : gUrsachen (unter anderem)

Unbedeutende nachfolgende Nullen werden aus dem Signifikanten entfernt, und der Dezimalpunkt wird ebenfalls entfernt, wenn keine verbleibenden Ziffern darauf folgen.

unutbu
quelle
27
Oh, fast! Manchmal formatiert es den Float in wissenschaftlicher Notation ("2.342E + 09") - ist es möglich, ihn auszuschalten, dh immer alle signifikanten Ziffern anzuzeigen?
TarGz
5
Warum verwenden, '{0:...}'.format(value)wenn Sie verwenden könnten format(value, '...')? Dadurch muss der Formatbezeichner nicht aus einer ansonsten leeren Vorlagenzeichenfolge analysiert werden.
Martijn Pieters
2
@MartijnPieters: Die winzigen Kosten für das Parsen des Formatbezeichners werden von anderen Overhead-AFAICT überschwemmt. In der Praxis haben meine lokalen Benchmarks für 3.6 (mit Funktionsumfang des Mikrobenchmarks zur genauen Modellierung von echtem Code) format(v, '2.5f')~ 10% länger gedauert als '{:2.5f}'.format(v). Selbst wenn dies nicht der Fall ist, verwende ich in der Regel das strMethodenformular, da es weniger zu ändern gibt, wenn ich es optimieren, zusätzliche Werte hinzufügen usw. muss. Natürlich haben wir ab 3.6 für die meisten Zwecke F-Strings. :-)
ShadowRanger
4
In Python 3.6 kann diese verkürzt werden , f"{var:g}"wo vareine Variable vom Typ float.
TheGreatCabbage
12

Was ist mit dem einfachsten und wahrscheinlich effektivsten Ansatz? Die Methode normalize () entfernt alle am weitesten rechts stehenden Nullen.

from decimal import Decimal

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

Funktioniert in Python 2 und Python 3 .

-- Aktualisiert --

Das einzige Problem, wie @ BobStein-VisiBone hervorhob, ist, dass Zahlen wie 10, 100, 1000 ... in exponentieller Darstellung angezeigt werden. Dies kann einfach mit der folgenden Funktion behoben werden:

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
quelle
2
Außer Decimal('10.0').normalize()wird'1E+1'
Bob Stein
11

Nachdem ich mir die Antworten auf einige ähnliche Fragen angesehen habe, scheint dies die beste Lösung für mich zu sein:

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

Meine Argumentation:

%g wird die wissenschaftliche Notation nicht los.

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

15 Dezimalstellen scheinen seltsames Verhalten zu vermeiden und haben viel Präzision für meine Bedürfnisse.

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

Ich hätte format(inputValue, '.15f').stattdessen verwenden können '%.15f' % inputValue, aber das ist etwas langsamer (~ 30%).

Ich hätte es gebrauchen können Decimal(inputValue).normalize(), aber das hat auch ein paar Probleme. Zum einen ist es VIEL langsamer (~ 11x). Ich fand auch heraus, dass es, obwohl es eine ziemlich hohe Präzision aufweist, bei der Verwendung immer noch unter Präzisionsverlust leidet normalize().

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

Am wichtigsten ist, dass ich immer noch Decimalvon einem konvertieren würde, floatwas dazu führen kann, dass Sie etwas anderes als die Nummer erhalten, die Sie dort eingegeben haben. Ich denke, Decimalfunktioniert am besten, wenn die Arithmetik erhalten bleibt Decimalund die Decimalmit einer Zeichenfolge initialisiert wird.

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

Ich bin mir sicher, dass das Präzisionsproblem von Decimal.normalize()mithilfe der Kontexteinstellungen an die Anforderungen angepasst werden kann, aber angesichts der ohnehin langsamen Geschwindigkeit, die keine lächerliche Präzision erfordert, und der Tatsache, dass ich immer noch von einem Float konvertieren und trotzdem an Präzision verlieren würde, habe ich es nicht getan Ich glaube nicht, dass es sich gelohnt hat, sie zu verfolgen.

Ich bin nicht besorgt über das mögliche "-0" -Ergebnis, da -0.0 eine gültige Gleitkommazahl ist und es wahrscheinlich sowieso selten vorkommt, aber da Sie erwähnt haben, dass Sie das String-Ergebnis so kurz wie möglich halten möchten, Sie könnte immer eine zusätzliche Bedingung bei sehr geringen zusätzlichen Geschwindigkeitskosten verwenden.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
PolyMesh
quelle
1
Funktioniert leider nur mit Zahlen mit weniger als ungefähr fünf oder mehr Ziffern links von der Dezimalstelle. floatToString(12345.6)gibt '12345.600000000000364'zum Beispiel zurück. Wenn Sie die 15 in %.15fauf eine niedrigere Zahl verringern, wird dies in diesem Beispiel gelöst. Dieser Wert muss jedoch immer weiter verringert werden, wenn die Zahl größer wird. Es könnte dynamisch basierend auf der Log-Base-10 der Zahl berechnet werden, aber das wird schnell sehr kompliziert.
JohnSpeeks
1
Eine Möglichkeit, dieses Problem zu lösen, könnte darin bestehen, die Länge der ganzen Zahl zu begrenzen (und nicht nur die Ziffern nach der Dezimalstelle):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith
@ JohnSpeeks Ich bin nicht sicher, ob dies vermeidbar ist. Es ist ein Nebeneffekt, dass schwebende Zahlen die Genauigkeit nicht darstellen können, wenn auf der linken Seite mehr Ziffern erforderlich sind. Soweit ich das beurteilen kann, ist die Zahl, die als Zeichenfolge ausgegeben wird, dieselbe Zahl, die als Float eingeht, oder zumindest die nächstgelegene Darstellung davon. >>>12345.600000000000364 == 12345.6 True
PolyMesh
Ich habe eine andere Lösung geschrieben .
Niitsuma
8

Hier ist eine Lösung, die für mich funktioniert hat. Es ist eine Mischung aus der Lösung von PolyMesh und der Verwendung der neuen .format() Syntax .

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

Ausgabe :

3
3
3
3.1
3.14
3.14
Kaushal Modi
quelle
Das einzige, was daran falsch ist, ist, dass Sie eine sinnvolle Anzahl von Dezimalstellen einstellen müssen. Je höher Sie es einstellen, desto präzisere Zahlen können Sie darstellen. Wenn Sie dies jedoch häufig tun, kann dies die Leistung beeinträchtigen.
Berühmte
1
Zusätzlich zum Kommentar von Berühmten funktioniert dies nicht für Floats mit größerer Präzision (z. B. 3.141), da die .2ffest codiert sind.
TrebledJ
3

Sie können einfach format () verwenden, um dies zu erreichen:

format(3.140, '.10g') Dabei ist 10 die gewünschte Präzision.

clel
quelle
2
>>> str(a if a % 1 else int(a))
Shameem
quelle
Meinst du nicht int(a) if a % 1 else a?
Berühmte
Lieber Beruic, deine Antwort führt zu einer negativen Antwort. a if a % 1 else int(a)ist richtig. Frage muss in Zeichenfolge ausgegeben werden, also habe ich gerade hinzugefügtstr
Shameem
Ah, ich verstehe es jetzt. a % 1ist wahr, weil es nicht Null ist. Ich habe es implizit und fälschlicherweise als wahrgenommen a % 1 == 0.
Berühmte
2

Während die Formatierung wahrscheinlich die pythonischste ist, gibt es hier eine alternative Lösung mit dem more_itertools.rstripTool.

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"

Die Zahl wird in eine Zeichenfolge konvertiert, bei der nachfolgende Zeichen, die ein Prädikat erfüllen, entfernt werden. Die Funktionsdefinition fmtist nicht erforderlich, wird hier jedoch zum Testen von Zusicherungen verwendet, die alle bestehen. Hinweis: Es funktioniert mit Zeichenfolgeneingaben und akzeptiert optionale Prädikate.

Siehe auch Details zu dieser Drittanbieter-Bibliothek more_itertools.

Pylang
quelle
1

Wenn Sie mit 3. und 3.0 leben können, die als "3.0" angezeigt werden, ist dies ein sehr einfacher Ansatz, bei dem Nullen aus Float-Darstellungen entfernt werden:

print("%s"%3.140)

(danke @ellimilial für den Hinweis auf die Ausnahmen)

drevicko
quelle
1
Aber print("%s"%3.0)tut es.
Ellimilial
1

Die Verwendung des QuantiPhy-Pakets ist eine Option. Normalerweise wird QuantiPhy verwendet, wenn mit Zahlen mit Einheiten und SI-Skalierungsfaktoren gearbeitet wird, es bietet jedoch eine Vielzahl nützlicher Formatierungsoptionen für Zahlen.

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

In dieser Situation wird keine E-Notation verwendet:

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

Eine Alternative, die Sie vielleicht bevorzugen, ist die Verwendung von SI-Skalierungsfaktoren, möglicherweise mit Einheiten.

    >>> 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
Herbst McClellan
quelle
0

OP möchte überflüssige Nullen entfernen und die resultierende Zeichenfolge so kurz wie möglich machen.

Ich finde, dass die exponentielle Formatierung von% g die resultierende Zeichenfolge für sehr große und sehr kleine Werte verkürzt. Das Problem tritt bei Werten auf, die keine Exponentialschreibweise benötigen, wie 128,0, die weder sehr groß noch sehr klein sind.

Hier ist eine Möglichkeit, Zahlen als kurze Zeichenfolgen zu formatieren, die nur dann die Exponentialnotation% g verwenden, wenn Decimal.normalize zu lange Zeichenfolgen erstellt. Dies ist möglicherweise nicht die schnellste Lösung (da Decimal.normalize verwendet wird).

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

Für Float können Sie Folgendes verwenden:

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

Probier es aus:

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

Für Dezimalstellen siehe Lösung hier: https://stackoverflow.com/a/42668598/5917543

Artem Skoretskiy
quelle
0

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

Ich benutze dies, um Floats zu formatieren, um Nullen zu verfolgen.

Martin Sonne
quelle
3143.93 -> 3.1e + 03
j35t3r
0

Hier ist die Antwort:

import numpy

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

Ausgabe "3.14" und "3"

trim='-' Entfernt sowohl die nachgestellten Nullen als auch die Dezimalstelle.

comport9
quelle
-1

Verwenden Sie% g mit einer ausreichend großen Breite, z. B. '% .99g'. Es wird in Festkommanotation für jede einigermaßen große Zahl gedruckt.

EDIT: es funktioniert nicht

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'
alexanderlukanin13
quelle
1
.99ist Präzision, nicht Breite; Ein bisschen nützlich, aber Sie können die tatsächliche Genauigkeit nicht auf diese Weise einstellen (außer sie selbst abzuschneiden).
Kos
-1

Sie können max()wie folgt verwenden:

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

Elig
quelle
1
Sie müssen den Fall berücksichtigen, in dem xnegativ ist. if x < 0: print(min(x), x) else : print(max(x), x)
Watou
Eine nützliche Methode, wenn ich json stringify machen möchte. float 1.0 wechselt zu int 1, so dass es genauso funktioniert wie in Javascript.
Pingze
-3

Sie können dies auf die meisten pythonischen Arten erreichen:

python3:

"{:0.0f}".format(num)
Lan Vukušič
quelle
Du hast recht. Der einfachste Weg ist, "{: g}" zu verwenden. Format (num)
Pyglouthon
-4

Umgang mit% f und Sie sollten setzen

% .2f

, wobei: .2f == .00 schwimmt.

Beispiel:

print "Preis:% .2f"% Preise [Produkt]

Ausgabe:

Preis: 1,50

Alex M.
quelle
1
Dies ist eindeutig nicht das, was die Frage will. Müssen abstimmen.
Folgeyang