Formatowanie jest zmienne bez zer końcowych

181

Jak sformatować liczbę zmiennoprzecinkową, aby nie zawierała zer na końcu? Innymi słowy, chcę, aby wynikowy ciąg był jak najkrótszy.

Na przykład:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
TarGz
źródło
Ten przykład nie ma żadnego sensu. 3.14 == 3.140- Są to te same liczby zmiennoprzecinkowe. W tym przypadku 3.140000 to ta sama liczba zmiennoprzecinkowa. Zero nie istnieje w ogóle.
S.Lott
33
@ S.Lott - Wydaje mi się, że problemem jest DRUKOWANIE liczby zmiennoprzecinkowej bez zer końcowych, a nie faktycznej równoważności dwóch liczb.
pokstad
1
@pokstad: W takim przypadku nie ma „zbędnego” zera. %0.2fi %0.3fsą dwoma formatami wymaganymi do utworzenia ostatnich liczb po lewej stronie. Użyj, %0.2faby utworzyć dwie ostatnie cyfry po prawej stronie.
S.Lott,
6
3.0 -> "3"jest nadal ważnym przypadkiem użycia. print( '{:,g}'.format( X )pracował dla mnie, aby uzyskać dane wyjściowe 3tam, gdzie X = 6 / 2i kiedy X = 5 / 2otrzymałem wynik zgodny z 2.5oczekiwaniami.
ShoeMaker
1
stare pytanie, ale .. print("%s"%3.140)daje ci to, czego chcesz. (Dodałem odpowiedź poniżej ...)
drevicko

Odpowiedzi:

178

Ja, zrobiłbym ('%f' % x).rstrip('0').rstrip('.')- gwarantuje formatowanie stałoprzecinkowe zamiast notacji naukowej itp. Tak, nie jest tak elegancki i elegancki %g, ale działa, (i nie wiem, jak zmusić, %gaby nigdy nie używać notacji naukowej; -).

Alex Martelli
źródło
6
Jedynym problemem jest '%.2f' % -0.0001to, że -0.00ostatecznie cię opuści -0.
Kos,
3
@ alexanderlukanin13, ponieważ domyślna precyzja wynosi 6, patrz docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.W powyższym rozwiązaniu musiałbyś użyć „% 0.7f”.
derenio
3
@derenio Dobra uwaga :-) Mogę tylko dodać, że podniesienie precyzji powyżej '%0.15f'jest złym pomysłem, ponieważ zaczynają się dziać dziwne rzeczy .
alexanderlukanin13
1
Jeśli jesteś w środku innego ciągu: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
odporny na uszkodzenia
1
@Alex Martelli, pozbądź się podwójnego rstrippolecenia. Po prostu użyj tego zamiast tego, aby usunąć zarówno kropkę ( .), jak i wszystkie końcowe zera w jednej operacji:('%f' % x).rstrip('.0')
Gabriel Staples
143

Możesz to %gosiągnąć:

'%g'%(3.140)

lub w przypadku Python 2.6 lub nowszego:

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

Z dokumentacji dotyczącejformat : gprzyczyn (między innymi)

nieznaczące końcowe zera [do] są usuwane ze znacznika, a kropka dziesiętna jest również usuwana, jeśli nie ma po niej pozostałych cyfr.

unutbu
źródło
28
Och, prawie! Czasami formatuje liczbę zmiennoprzecinkową w notacji naukowej („2.342E + 09”) - czy można ją wyłączyć, tzn. Zawsze wyświetlać wszystkie cyfry znaczące?
TarGz
5
'{0:...}'.format(value)Po co korzystać, kiedy można korzystać format(value, '...')? Pozwala to uniknąć konieczności analizowania specyfikatora formatu z ciągu szablonu, który w innym przypadku byłby pusty.
Martijn Pieters
2
@MartijnPieters: Drobny koszt parsowania specyfikatora formatu jest zalany przez inne koszty ogólne AFAICT; w praktyce moje lokalne testy porównawcze na 3.6 (z funkcją scoping mikrobenchmarka w celu dokładnego modelowania rzeczywistego kodu) format(v, '2.5f')zajęły ~ 10% dłużej niż '{:2.5f}'.format(v). Nawet jeśli tak się nie stało, zwykle używam strformularza metody, ponieważ kiedy muszę go ulepszyć, dodać do niego dodatkowe wartości itp., Jest mniej do zmiany. Oczywiście od wersji 3.6 mamy większość strun f-string. :-)
ShadowRanger
5
Pythona 3.6, może być skrócony do f"{var:g}"gdzie varjest zmienną pływak.
TheGreatCabbage
12

Co powiesz na wypróbowanie najłatwiejszego i prawdopodobnie najbardziej skutecznego podejścia? Metoda normalize () usuwa wszystkie skrajne po prawej stronie zera.

from decimal import Decimal

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

Działa w Python 2 i Python 3 .

- Zaktualizowano -

Jedynym problemem, jak wskazał @ BobStein-VisiBone, jest to, że liczby takie jak 10, 100, 1000 ... będą wyświetlane w postaci wykładniczej. Można to łatwo naprawić za pomocą następującej funkcji:

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
źródło
2
Z wyjątkiem Decimal('10.0').normalize()staje się'1E+1'
Bob Stein
11

Po przejrzeniu odpowiedzi na kilka podobnych pytań wydaje mi się to najlepszym rozwiązaniem:

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

Moje rozumowanie:

%g nie pozbywa się notacji naukowej.

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

15 miejsc po przecinku wydaje się unikać dziwnego zachowania i ma dużą precyzję na moje potrzeby.

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

Mógłbym użyć format(inputValue, '.15f').zamiast '%.15f' % inputValue, ale to trochę wolniej (~ 30%).

Mógłbym użyć Decimal(inputValue).normalize(), ale ma to również kilka problemów. Po pierwsze, jest DUŻO wolniejszy (~ 11x). Odkryłem również, że chociaż ma dość dużą precyzję, nadal cierpi z powodu utraty precyzji podczas używania normalize().

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

Co najważniejsze, nadal będę się zmieniał Decimalz tego, floatco może sprawić, że skończysz na czymś innym niż liczba, którą tam umieściłeś. Myślę, że Decimaldziała najlepiej, gdy arytmetyka pozostaje w środku Decimali Decimaljest inicjalizowana ciągiem znaków.

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

Jestem pewien, że kwestię precyzji Decimal.normalize()można dopasować do tego, co jest potrzebne przy użyciu ustawień kontekstowych, ale biorąc pod uwagę już małą prędkość i niepotrzebną niedorzeczną precyzję, a także fakt, że i tak będę konwertować z liczby zmiennoprzecinkowej i tracę precyzję, nie zrobiłem tego nie sądzę, że warto było to kontynuować.

Nie przejmuję się możliwym wynikiem „-0”, ponieważ -0.0 jest prawidłową liczbą zmiennoprzecinkową i prawdopodobnie byłoby to rzadkie zjawisko, ale ponieważ wspomniałeś, że chcesz, aby wynik łańcucha był jak najkrótszy, zawsze może zastosować dodatkowy warunek przy bardzo niewielkim koszcie dodatkowej prędkości.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
PolyMesh
źródło
1
Niestety działa tylko z liczbami z mniej niż z grubsza) pięcioma lub więcej cyframi po lewej stronie miejsca dziesiętnego. floatToString(12345.6)zwraca '12345.600000000000364'na przykład. Zmniejszenie 15 %.15fdo mniejszej liczby rozwiązuje to w tym przykładzie, ale wartość ta musi być zmniejszana coraz bardziej, gdy liczba staje się większa. Może być obliczany dynamicznie na podstawie log-base-10 liczby, ale szybko staje się to bardzo skomplikowane.
JohnSpeeks
1
Jednym ze sposobów rozwiązania tego problemu może być ograniczenie długości liczby całkowitej (a nie tylko cyfr po przecinku):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith
@JohnSpeeks Nie jestem pewien, czy można tego uniknąć. Jest to efekt uboczny polegający na tym, że liczby zmiennoprzecinkowe nie są w stanie przedstawić dokładności, jeśli po lewej stronie potrzeba więcej cyfr. Z tego, co mogę powiedzieć, liczba, która pojawia się jako ciąg, jest tą samą liczbą, która wchodzi jako liczba zmiennoprzecinkowa, lub przynajmniej najbliższą jej reprezentacją. >>>12345.600000000000364 == 12345.6 True
PolyMesh
Napisałem inne rozwiązanie .
niitsuma
8

Oto rozwiązanie, które zadziałało dla mnie. Jest to mieszanka roztworu przez PolyMesh i stosowania nowej .format() składni .

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

Wyjście :

3
3
3
3.1
3.14
3.14
Kaushal Modi
źródło
Jedyną wadą tego jest to, że musisz ustawić rozsądną liczbę cyfr dziesiętnych. Im wyżej ją ustawisz, tym dokładniejsze liczby możesz przedstawić, ale jeśli będziesz to robił dużo, może to obniżyć wydajność.
beruic
1
Dodając do komentarza beruica, nie działa to dla pływaków o większej precyzji (np. 3.141), Ponieważ .2fjest na stałe zakodowany.
TrebledJ
3

Aby to osiągnąć, możesz po prostu użyć formatu ():

format(3.140, '.10g') gdzie 10 to pożądana precyzja.

clel
źródło
2
>>> str(a if a % 1 else int(a))
Shameem
źródło
Nie masz na myśli int(a) if a % 1 else a?
beruic
Drogi Beruicu, twoja odpowiedź daje odpowiedź przeczącą. a if a % 1 else int(a)jest poprawne. Pytanie wymaga danych wyjściowych w ciągu, więc właśnie dodałemstr
Shameem,
Ach, rozumiem teraz. a % 1jest prawdą, ponieważ jest niezerowe. Ja domyślnie i błędnie postrzegałem to jako a % 1 == 0.
beruic
2

Podczas gdy formatowanie jest prawdopodobne w większości Pythonów, tutaj jest alternatywne rozwiązanie za pomocą tego more_itertools.rstripnarzędzia.

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"

Liczba jest konwertowana na ciąg, który jest pozbawiony końcowych znaków, które spełniają predykat. Definicja funkcji fmtnie jest wymagana, ale służy tutaj do testowania twierdzeń, które wszystkie przekazują. Uwaga: działa na wejściach łańcuchowych i akceptuje predykaty opcjonalne.

Zobacz także szczegółowe informacje na temat tej biblioteki stron trzecich, more_itertools.

pylang
źródło
1

Jeśli możesz żyć z wersjami 3. i 3.0 wyświetlanymi jako „3.0”, bardzo proste podejście, które usuwa zera z pływających reprezentacji:

print("%s"%3.140)

(dzięki @ellimilial za wskazanie wyjątków)

drevicko
źródło
1
Ale print("%s"%3.0)robi.
ellimilial
1

Korzystanie z pakietu QuantiPhy jest opcją. Zazwyczaj QuantiPhy jest używane podczas pracy z liczbami z jednostkami i współczynnikami skali SI, ale ma wiele fajnych opcji formatowania liczb.

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

I nie będzie używać notacji elektronicznej w tej sytuacji:

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

Alternatywą, którą możesz preferować, jest użycie współczynników skali SI, być może z jednostkami.

    >>> 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
Autumn McClellan
źródło
0

OP chce usunąć zbędne zera i uczynić otrzymany ciąg tak krótkim, jak to możliwe.

Uważam, że formatowanie wykładnicze% g skraca wynikowy ciąg znaków dla bardzo dużych i bardzo małych wartości. Problem pojawia się w przypadku wartości, które nie wymagają notacji wykładniczej, takich jak 128.0, która nie jest ani bardzo duża, ani bardzo mała.

Oto jeden ze sposobów formatowania liczb jako krótkich ciągów, które wykorzystują notację wykładniczą% g tylko wtedy, gdy Decimal.normalize tworzy ciągi, które są zbyt długie. To może nie być najszybsze rozwiązanie (ponieważ używa 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
źródło
0

Dla float możesz użyć tego:

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

Sprawdź to:

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

W przypadku systemu dziesiętnego zobacz rozwiązanie tutaj: https://stackoverflow.com/a/42668598/5917543

Artem Skoretskiy
źródło
0

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

Używam tego do formatowania liczb zmiennoprzecinkowych do śledzenia zer.

Martin Sun
źródło
3143.93 -> 3.1e + 03
j35t3r
0

Oto odpowiedź:

import numpy

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

wyjście „3.14” i „3”

trim='-' usuwa zarówno końcowe zera, jak i dziesiętne.

comport9
źródło
-1

Użyj% g o wystarczająco dużej szerokości, na przykład „% .99g”. Będzie drukowany w notacji stałej dla dowolnej dość dużej liczby.

EDYCJA: to nie działa

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'
alexanderlukanin13
źródło
1
.99to precyzja, a nie szerokość; jest to przydatne, ale nie można w ten sposób ustawić rzeczywistej precyzji (poza obcięciem jej samodzielnie).
Kos,
-1

Możesz użyć w max()ten sposób:

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

elig
źródło
1
musisz rozważyć przypadek, w którym xjest negatywny. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix
Przydatna metoda, gdy chcę zrobić json stringify. zmienna liczba zmiennoprzecinkowa 1.0 na int 1, więc działa tak samo jak w javascript.
pingze
-3

Możesz to osiągnąć w najbardziej pythonowy sposób:

python3:

"{:0.0f}".format(num)
Lan Vukušič
źródło
Masz rację. Najprostszym sposobem jest użycie formatu „{: g}” (num)
Pyglouthon,
-4

Obsługa% f i powinieneś umieścić

% .2f

, gdzie: .2f == .00 zmiennoprzecinkowe.

Przykład:

drukuj „Cena:% .2f”% ceny [produkt]

wynik:

Cena: 1,50

Alex M.
źródło
1
To jest wyraźnie stwierdzone, nie to, czego chce pytanie. Potrzebujesz głosować.
odcinek