Memformat mengapung tanpa membuntuti nol

181

Bagaimana saya bisa memformat float sehingga tidak mengandung nol tambahan? Dengan kata lain, saya ingin string yang dihasilkan sesingkat mungkin.

Sebagai contoh:

3 -> "3"
3. -> "3"
3.0 -> "3"
3.1 -> "3.1"
3.14 -> "3.14"
3.140 -> "3.14"
TarGz
sumber
Contoh itu sama sekali tidak masuk akal. 3.14 == 3.140- Mereka nomor floating point yang sama . Dalam hal ini 3.140000 adalah angka floating-point yang sama. Angka nol tidak ada sejak awal.
S.Lott
33
@ S.Lott - Saya pikir masalahnya adalah MENCETAK angka float tanpa nol tambahan, bukan kesetaraan sebenarnya dari dua angka.
pokstad
1
@pokstad: Dalam hal ini, tidak ada nol "berlebihan". %0.2fdan %0.3fdua format yang diperlukan untuk menghasilkan angka terakhir di sebelah kiri. Gunakan %0.2funtuk menghasilkan dua angka terakhir di sebelah kanan.
S.Lott
6
3.0 -> "3"masih merupakan use case yang valid. print( '{:,g}'.format( X )bekerja untuk saya untuk output di 3mana X = 6 / 2dan kapan X = 5 / 2saya mendapatkan output 2.5seperti yang diharapkan.
Pembuat Sepatu
1
pertanyaan lama, tapi .. print("%s"%3.140)memberi Anda apa yang Anda inginkan. (Saya menambahkan jawaban di bawah ...)
drevicko

Jawaban:

178

Saya, saya akan lakukan ('%f' % x).rstrip('0').rstrip('.')- menjamin pemformatan titik tetap daripada notasi ilmiah, dll. Ya, tidak selipis dan seanggun %g, tapi, itu berfungsi (dan saya tidak tahu bagaimana memaksa %guntuk tidak pernah menggunakan notasi ilmiah; -).

Alex Martelli
sumber
6
Satu-satunya masalah dengan itu '%.2f' % -0.0001akan meninggalkan Anda -0.00dan akhirnya -0.
Kos
3
@ alexanderlukanin13 karena presisi default adalah 6, lihat docs.python.org/2/library/string.html : 'f' Fixed point. Displays the number as a fixed-point number. The default precision is 6.Anda harus menggunakan '% 0.7f' dalam solusi di atas.
derenio
3
@derenio Poin bagus :-) Saya hanya bisa menambahkan bahwa meningkatkan presisi di atas '%0.15f'adalah ide yang buruk, karena hal-hal aneh mulai terjadi.
alexanderlukanin13
1
Jika Anda berada di tengah-tengah string lain: print('In the middle {} and something else'.format('{:f}'.format(a).rstrip('0')))
toleran terhadap kesalahan
1
@Alex Martelli, singkirkan rstripperintah ganda . Gunakan ini sebagai gantinya untuk menghapus titik ( .) dan semua nol tambahan setelahnya dalam satu operasi:('%f' % x).rstrip('.0')
Gabriel Staples
143

Anda dapat menggunakan %guntuk mencapai ini:

'%g'%(3.140)

atau, untuk Python 2.6 atau lebih baik:

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

Dari dokumen untukformat : gpenyebab (antara lain)

nol trailing tidak signifikan [harus] dihapus dari signifikansi, dan titik desimal juga dihapus jika tidak ada digit yang tersisa mengikutinya.

unutbu
sumber
28
Oh, hampir! Terkadang memformat float dalam notasi ilmiah ("2.342E + 09") - apakah mungkin untuk mematikannya, yaitu selalu menunjukkan semua digit signifikan?
TarGz
5
Mengapa menggunakan '{0:...}'.format(value)saat Anda bisa menggunakannya format(value, '...')? Itu menghindari harus mem-parsing specifier format dari string template yang dinyatakan kosong.
Martijn Pieters
2
@ MartijnPieters: Biaya sangat kecil untuk menguraikan format specifier dibanjiri oleh overhead AFAICT lainnya; dalam praktiknya, tolok ukur lokal saya di 3.6 (dengan fungsi pelingkupan dari microbenchmark untuk memodelkan kode nyata secara akurat) telah format(v, '2.5f')memakan waktu ~ 10% lebih lama dari '{:2.5f}'.format(v). Bahkan jika tidak, saya cenderung menggunakan strformulir metode karena ketika saya perlu men-tweak, menambahkan nilai tambahan, dll, ada sedikit perubahan. Tentu saja, pada 3.6 kita memiliki f-string untuk sebagian besar tujuan. :-)
ShadowRanger
5
Dalam Python 3.6, ini dapat disingkat menjadi f"{var:g}"mana varadalah variabel float.
TheGreatCabbage
12

Bagaimana dengan mencoba pendekatan yang paling mudah dan mungkin paling efektif? Metode menormalkan () menghapus semua nol trailing paling kanan.

from decimal import Decimal

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

Bekerja di Python 2 dan Python 3 .

- Diperbarui -

Satu-satunya masalah seperti yang ditunjukkan oleh @ BobStein-VisiBone, adalah angka-angka seperti 10, 100, 1000 ... akan ditampilkan dalam representasi eksponensial. Ini dapat dengan mudah diperbaiki menggunakan fungsi berikut sebagai gantinya:

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
sumber
2
Kecuali Decimal('10.0').normalize()menjadi'1E+1'
Bob Stein
11

Setelah mencari jawaban atas beberapa pertanyaan serupa, ini tampaknya menjadi solusi terbaik bagi saya:

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

Alasan saya:

%g tidak menghilangkan notasi ilmiah.

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

15 tempat desimal tampaknya menghindari perilaku aneh dan memiliki banyak ketepatan untuk kebutuhan saya.

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

Saya bisa menggunakan format(inputValue, '.15f').bukan '%.15f' % inputValue, tapi itu sedikit lebih lambat (~ 30%).

Saya bisa menggunakan Decimal(inputValue).normalize(), tetapi ini memiliki beberapa masalah juga. Untuk satu, itu BANYAK lebih lambat (~ 11x). Saya juga menemukan bahwa meskipun memiliki presisi yang sangat bagus, ia masih mengalami kehilangan presisi saat menggunakan normalize().

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

Yang paling penting, saya masih akan mengkonversi Decimaldari floatyang dapat membuat Anda berakhir dengan sesuatu selain nomor yang Anda masukkan di sana. Saya pikir Decimalbekerja paling baik ketika aritmatika tetap Decimaldan Decimaldiinisialisasi dengan string.

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

Saya yakin masalah presisi Decimal.normalize()dapat disesuaikan dengan apa yang diperlukan menggunakan pengaturan konteks, tetapi mengingat kecepatan yang sudah lambat dan tidak memerlukan presisi yang konyol dan fakta bahwa saya masih akan mengkonversi dari float dan kehilangan presisi, saya tidak pikir itu layak dikejar.

Saya tidak khawatir dengan kemungkinan hasil "-0" karena -0.0 adalah angka floating point yang valid dan mungkin akan menjadi kejadian yang jarang terjadi, tetapi karena Anda memang menyebutkan Anda ingin menjaga hasil string sesingkat mungkin, Anda selalu dapat menggunakan persyaratan ekstra dengan biaya kecepatan ekstra yang sangat kecil.

def floatToString(inputValue):
    result = ('%.15f' % inputValue).rstrip('0').rstrip('.')
    return '0' if result == '-0' else result
PolyMesh
sumber
1
Sayangnya hanya bekerja dengan angka dengan kurang dari kurang lebih) lima digit atau lebih di sebelah kiri tempat desimal. floatToString(12345.6)kembali '12345.600000000000364'misalnya. Mengurangi angka 15 %.15fke angka yang lebih rendah memecahkannya dalam contoh ini, tetapi nilai itu perlu diturunkan lebih dan lebih lagi ketika jumlahnya semakin besar. Ini bisa dihitung secara dinamis berdasarkan log-base-10 dari angka tersebut, tetapi itu dengan cepat menjadi sangat rumit.
JohnSpeeks
1
Salah satu cara untuk memecahkan masalah itu mungkin dengan membatasi panjang seluruh bilangan (bukan hanya angka setelah desimal):result = ('%15f' % val).rstrip('0').rstrip('.').lstrip(' ')
Timothy Smith
@ JohnSpeeks Saya tidak yakin ini bisa dihindari. Ini adalah efek samping dari angka mengambang yang tidak dapat mewakili akurasi jika diperlukan lebih banyak angka di sisi kiri. Dari apa yang bisa saya katakan, angka yang keluar sebagai string adalah angka yang sama yang masuk sebagai pelampung, atau setidaknya representasi terdekat dari itu. >>>12345.600000000000364 == 12345.6 True
PolyMesh
Saya menulis solusi lain .
niitsuma
8

Inilah solusi yang bekerja untuk saya. Ini adalah campuran dari solusi oleh PolyMesh dan penggunaan .format() sintaks baru .

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

Output :

3
3
3
3.1
3.14
3.14
Kaushal Modi
sumber
Satu-satunya hal yang salah dengan ini adalah Anda harus menetapkan angka desimal yang masuk akal. Semakin tinggi Anda mengaturnya, angka yang lebih tepat dapat Anda wakili, tetapi jika Anda sering melakukan ini, itu dapat menurunkan kinerja.
beruic
1
Menambah komentar beruic, ini tidak berfungsi untuk float dengan presisi lebih tinggi (misalnya 3.141) karena .2fhard-coded.
TrebledJ
3

Anda cukup menggunakan format () untuk mencapai ini:

format(3.140, '.10g') di mana 10 adalah presisi yang Anda inginkan.

clel
sumber
2
>>> str(a if a % 1 else int(a))
Shameem
sumber
Bukankah maksud Anda int(a) if a % 1 else a?
beruic
Dear Beruic, jawaban Anda menghasilkan jawaban negatif. a if a % 1 else int(a)benar. Pertanyaan membutuhkan output dalam string, Jadi saya baru saja menambahkanstr
Shameem
Ah, saya mengerti sekarang. a % 1adalah kebenaran karena itu bukan nol. Saya secara implisit dan keliru menganggapnya sebagai a % 1 == 0.
beruic
2

Sementara memformat sepertinya cara Pythonic yang paling, di sini adalah solusi alternatif menggunakan more_itertools.rstripalat.

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"

Angka tersebut dikonversi menjadi string, yang dilucuti dari karakter trailing yang memenuhi predikat. Definisi fungsi fmttidak diperlukan, tetapi digunakan di sini untuk menguji pernyataan, yang semuanya lulus. Catatan: ini bekerja pada input string dan menerima predikat opsional.

Lihat juga detail di perpustakaan pihak ketiga ini more_itertools,.

pylang
sumber
1

Jika Anda dapat hidup dengan 3. dan 3.0 yang muncul sebagai "3.0", pendekatan yang sangat sederhana yang melucuti nol dari representasi float:

print("%s"%3.140)

(terima kasih @ellimilial karena menunjukkan pengecualian)

drevicko
sumber
1
Namun print("%s"%3.0)demikian.
ellimilial
1

Menggunakan paket QuantiPhy adalah sebuah opsi. Biasanya QuantiPhy digunakan ketika bekerja dengan angka dengan unit dan faktor skala SI, tetapi memiliki berbagai opsi pemformatan angka yang bagus.

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

Dan itu tidak akan menggunakan e-notasi dalam situasi ini:

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

Alternatif yang mungkin Anda sukai adalah menggunakan faktor skala SI, mungkin dengan satuan.

    >>> 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
Musim Gugur McClellan
sumber
0

OP ingin menghapus nol superflouous dan membuat string yang dihasilkan sesingkat mungkin.

Saya menemukan format% g eksponensial memendekkan string yang dihasilkan untuk nilai yang sangat besar dan sangat kecil. Masalahnya muncul untuk nilai yang tidak memerlukan notasi eksponensial, seperti 128.0, yang tidak terlalu besar atau sangat kecil.

Berikut adalah salah satu cara untuk memformat angka sebagai string pendek yang menggunakan notasi eksponensial% g hanya ketika Decimal.normalisasi membuat string yang terlalu panjang. Ini mungkin bukan solusi tercepat (karena memang menggunakan Decimal.normalisasi)

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

Untuk float Anda bisa menggunakan ini:

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

Menguji:

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

Untuk solusi Desimal lihat di sini: https://stackoverflow.com/a/42668598/5917543

Artem Skoretskiy
sumber
0

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

Saya menggunakan ini untuk memformat mengapung untuk melacak nol.

martin sun
sumber
3143.93 -> 3.1e + 03
j35t3r
0

Inilah jawabannya:

import numpy

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

output "3.14" dan "3"

trim='-' menghapus kedua trailing zero, dan desimal.

membawakan9
sumber
-1

Gunakan% g dengan lebar yang cukup besar, misalnya '% .99g'. Ini akan mencetak dalam notasi titik tetap untuk nomor yang cukup besar.

EDIT: tidak bekerja

>>> '%.99g' % 0.0000001
'9.99999999999999954748111825886258685613938723690807819366455078125e-08'
alexanderlukanin13
sumber
1
.99presisi, bukan lebar; agak berguna tetapi Anda tidak bisa mengatur presisi aktual dengan cara ini (selain memotongnya sendiri).
Kos
-1

Anda bisa menggunakan max()seperti ini:

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

memenuhi syarat
sumber
1
Anda harus mempertimbangkan kasus di mana xnegatif. if x < 0: print(min(x), x) else : print(max(x), x)
ThunderPhoenix
Metode yang berguna ketika saya ingin melakukan json stringify. float 1.0 berubah menjadi int 1, jadi performanya sama seperti di javascript.
pingze
-3

Anda dapat mencapainya dengan cara pythonic seperti itu:

python3:

"{:0.0f}".format(num)
Lan Vukušič
sumber
Kamu benar. Cara termudah adalah dengan menggunakan format "{: g}" (num)
Pyglouthon
-4

Menangani% f dan Anda harus meletakkan

% .2f

, di mana: .2f == .00 floats.

Contoh:

cetak "Harga:% .2f"% harga [produk]

keluaran:

Harga: 1,50

Alex M.
sumber
1
Ini jelas dinyatakan bukan apa yang diinginkan pertanyaan. Perlu downvote.
episodeyang