Saya ingin memformat angka berikut menjadi angka di sebelahnya dengan java:
1000 to 1k
5821 to 5.8k
10500 to 10k
101800 to 101k
2000000 to 2m
7800000 to 7.8m
92150000 to 92m
123200000 to 123m
Angka di sebelah kanan akan panjang atau bilangan bulat di sebelah kiri akan menjadi string. Bagaimana saya harus mendekati ini. Saya sudah melakukan sedikit algoritme untuk ini, tetapi saya pikir mungkin sudah ada sesuatu yang ditemukan di sana yang melakukan pekerjaan yang lebih baik dan tidak memerlukan pengujian tambahan jika saya mulai berurusan dengan miliaran dan triliunan :)
Persyaratan tambahan:
- Formatnya harus maksimal 4 karakter
- Di atas berarti 1.1k OK 11.2k tidak. Sama untuk 7.8m tidak apa-apa 19.1m tidak. Hanya satu digit sebelum titik desimal diizinkan memiliki titik desimal. Dua digit sebelum titik desimal berarti bukan digit setelah titik desimal.
- Tidak diperlukan pembulatan. (Angka yang ditampilkan dengan k dan m ditambahkan lebih dari pengukur analog yang menunjukkan aproksimasi bukan artikel logika yang tepat. Oleh karena itu pembulatan tidak relevan terutama karena sifat variabel daripada dapat meningkatkan atau menurunkan beberapa digit bahkan ketika Anda melihat hasil dalam cache.)
java
number-formatting
Mat B.
sumber
sumber
No rounding is necessary
ini tampaknya tidak masuk akal bagi saya. Apakah hanya untuk memperumit masalah? Bukankah lebih baik untuk mengulangi iniRounding is not necessary, but welcome
?Jawaban:
Berikut ini adalah solusi yang bekerja untuk setiap nilai yang panjang dan yang menurut saya cukup mudah dibaca (logika inti dilakukan pada tiga baris terbawah dari
format
metode ini).Ini memanfaatkan
TreeMap
untuk menemukan akhiran yang sesuai. Secara mengejutkan ini lebih efisien daripada solusi sebelumnya yang saya tulis yang menggunakan array dan lebih sulit dibaca.Kode uji
sumber
-5821
harus diformat sebagai-5k
, bukan sebagai-5.8k
.-
untuk mempertahankan jumlah digit signifikan yang sama. Ada pilihan lain ...Saya tahu, ini lebih mirip program C, tapi sangat ringan!
Ini menghasilkan:
sumber
Berikut solusi yang menggunakan notasi teknik DecimalFormat:
Keluaran:
sumber
Perlu beberapa perbaikan, tetapi: StrictMath to the rescue!
Anda bisa meletakkan akhiran dalam sebuah String atau array dan mengambilnya berdasarkan kekuatan, atau sesuatu seperti itu.
Divisi ini juga dapat dikelola di sekitar daya, saya pikir hampir semuanya tentang nilai daya. Semoga ini bisa membantu!
output:
sumber
Masalah dengan Jawaban Saat Ini
Solusi Java
Solusi ini (perpanjangan dari jawaban ini ) mengatasi masalah di atas.
Solusi Groovy
Solusinya awalnya ditulis dalam Groovy seperti yang ditunjukkan di bawah ini.
Tes (Groovy)
Tes ditulis dalam Groovy tetapi dapat digunakan untuk memverifikasi kelas Java atau Groovy (karena keduanya memiliki nama dan API yang sama).
sumber
The ICU lib memiliki formatter berbasis aturan untuk nomor, yang dapat digunakan untuk nomor spellout dll Saya pikir menggunakan ICU akan memberikan solusi yang dapat dibaca dan maintanable.
[Pemakaian]
Kelas yang tepat adalah RuleBasedNumberFormat. Format itu sendiri dapat disimpan sebagai file terpisah (atau sebagai String konstan, IIRC).
Contoh dari http://userguide.icu-project.org/formatparse/numbers
Halaman yang sama menunjukkan angka-angka Romawi, jadi saya kira kasus Anda juga harus dimungkinkan.
sumber
CompactDecimalFormat
. API Level 24+Dengan Java-12 + , Anda dapat menggunakan
NumberFormat.getCompactNumberInstance
format angka. Anda dapat membuat yangNumberFormat
pertama sebagaidan kemudian menggunakannya untuk
format
:sumber
Penting: Menjawab jawaban yang
double
akan gagal untuk angka suka99999999999999999L
dan kembali100P
bukan99P
karenadouble
menggunakanIEEE
standar :Solusi ini memotong angka yang tidak diinginkan dan bekerja untuk semua
long
nilai . Implementasi sederhana tetapi performan (perbandingan di bawah). -120k tidak dapat diekspresikan dengan 4 karakter, bahkan -0.1M terlalu panjang, itu sebabnya untuk angka negatif 5 karakter harus baik-baik saja:Tes di
else if
awal adalah keharusan karena min adalah-(2^63)
dan maks adalah(2^63)-1
dan karena itu tugasnumber = -number
akan gagal jikanumber == Long.MIN_VALUE
. Jika kita harus melakukan pemeriksaan, maka kita juga bisa memasukkan angka sebanyak mungkin, bukan hanya memeriksanumber == Long.MIN_VALUE
.Perbandingan implementasi ini dengan orang yang mendapat upvotes terbanyak (dikatakan sebagai yang tercepat saat ini) menunjukkan bahwa itu lebih dari 5 kali lebih cepat (tergantung pada pengaturan tes, tetapi dengan lebih banyak angka, gain bertambah besar dan implementasi ini memiliki untuk melakukan lebih banyak pemeriksaan karena menangani semua kasus, jadi jika yang lain diperbaiki perbedaannya akan menjadi lebih besar). Ini sangat cepat karena tidak ada operasi floating point, tidak ada logaritma, tidak ada daya, tidak ada rekursi, tidak ada regex, tidak ada formatters canggih dan minimalisasi jumlah objek yang dibuat.
Inilah program pengujiannya:
Output yang mungkin:
2309 vs. 11591
(hampir sama ketika hanya menggunakan angka positif dan jauh lebih ekstrim ketika membalikkan urutan eksekusi, mungkin itu ada hubungannya dengan pengumpulan sampah)sumber
Berikut ini adalah implementasi singkat tanpa rekursi dan hanya loop yang sangat kecil. Tidak bekerja dengan angka negatif tetapi mendukung semua positif
long
hinggaLong.MAX_VALUE
:Output:
Saya juga melakukan beberapa tolok ukur yang sangat sederhana (memformat 10 juta rindu acak) dan ini jauh lebih cepat daripada implementasi Elia dan sedikit lebih cepat dari implementasi assylias.
sumber
Untuk siapa saja yang ingin membulatkan tekad. Ini adalah solusi hebat dan mudah dibaca, yang memanfaatkan perpustakaan Java.Lang.Math
sumber
Kode berikut menunjukkan bagaimana Anda dapat melakukan ini dengan ekspansi mudah dalam pikiran.
"Sihir" sebagian besar terletak pada
makeDecimal
fungsi yang, untuk nilai-nilai yang benar diteruskan, menjamin Anda tidak akan pernah memiliki lebih dari empat karakter dalam output.Pertama-tama mengekstraksi seluruh dan sepersepuluh bagian untuk pembagi yang diberikan jadi, misalnya,
12,345,678
dengan pembagi1,000,000
akan memberikanwhole
nilai12
dantenths
nilai3
.Dari itu, ia dapat memutuskan apakah itu menghasilkan hanya seluruh bagian atau keseluruhan dan kesepuluh bagian, menggunakan aturan:
Kode untuk itu adalah sebagai berikut:
Kemudian, ini masalah sederhana memanggil fungsi pembantu dengan nilai-nilai yang benar, termasuk beberapa konstanta untuk membuat hidup lebih mudah bagi pengembang:
Fakta bahwa
makeDecimal
fungsi melakukan pekerjaan kasar berarti memperluas di luar999,999,999
hanya masalah menambahkan garis tambahanXlat
, begitu mudah bahwa saya telah melakukannya untuk Anda.Final
return
inXlat
tidak membutuhkan persyaratan karena nilai terbesar yang dapat Anda tahan dalam 64-bit lama ditandatangani hanya sekitar 9,2 triliun.Tetapi jika, dengan persyaratan aneh, Oracle memutuskan untuk menambahkan tipe 128-bit
longer
atau tipe 1024-bitdamn_long
, Anda akan siap untuk itu :-)Dan, akhirnya, sedikit test harness yang dapat Anda gunakan untuk memvalidasi fungsionalitas.
Anda dapat melihat dari hasilnya bahwa itu memberi Anda apa yang Anda butuhkan:
sumber
Saya tidak tahu apakah itu pendekatan terbaik tetapi, inilah yang saya lakukan.
--- kode ---
sumber
Fungsi saya untuk mengonversi angka besar ke angka kecil (dengan 2 digit). Anda dapat mengubah jumlah digit perubahan
#.##
diDecimalFormat
Pengujian
Semoga ini bisa membantu
sumber
Java saya sudah berkarat, tapi beginilah cara saya mengimplementasikannya di C #:
Akan mudah untuk menyesuaikan ini untuk menggunakan CS kilo (1.024) daripada metrik kilo, atau untuk menambahkan lebih banyak unit. Ini format 1.000 sebagai "1.0 k" daripada "1 k", tapi saya percaya itu tidak penting.
Untuk memenuhi persyaratan yang lebih spesifik "tidak lebih dari empat karakter", hapus spasi sebelum sufiks dan sesuaikan blok tengah seperti ini:
sumber
ToString
metode ini tidak ada di Jawa - Anda memerlukan NumberFormat yang dapat membuat masalah lain (peka lokal, dll.).Kesukaanku. Anda dapat menggunakan "k" dan seterusnya sebagai indikator untuk desimal juga, seperti yang umum dalam domain elektronik. Ini akan memberi Anda digit tambahan tanpa ruang tambahan
Kolom kedua mencoba menggunakan sebanyak mungkin digit
Ini kodenya
sumber
Tetap setia pada komentar saya bahwa saya akan menghargai keterbacaan di atas kinerja, inilah versi di mana harus jelas apa yang terjadi (dengan asumsi Anda telah menggunakan
BigDecimal
sebelumnya) tanpa komentar berlebihan (saya percaya pada kode yang mendokumentasikan diri), tanpa khawatir tentang kinerja (Karena saya tidak bisa menggambarkan skenario di mana Anda ingin melakukan ini jutaan kali bahkan kinerja menjadi pertimbangan).Versi ini:
BigDecimal
s untuk presisi dan untuk menghindari masalah pembulatanHALF_UP
seperti dalam tesREQUIRED_PRECISION
)enum
untuk mendefinisikan ambang batas, yaitu dengan mudah dapat disesuaikan untuk menggunakan KB / MB / GB / TB daripada k / m / b / t, dll, dan tentu saja dapat diperluas melampauiTRILLION
jika diperlukanThreshold.java :
NumberShortener.java :
(Batalkan komentar
println
atau perubahan untuk menggunakan logger favorit Anda untuk melihat apa yang dilakukannya.)Dan akhirnya, tes di NumberShortenerTest (plain JUnit 4):
Jangan ragu untuk menunjukkan di komentar jika saya melewatkan kasus uji yang signifikan atau jika nilai yang diharapkan harus disesuaikan.
sumber
TreeMap
pendekatannya. "Keterbacaan" itu subjektif, tentu saja. ;-) Sekarang bagaimana jika seseorang ingin membulatkan berbeda daripada memotong dalam versi Anda? (Misalnya, ketika menggunakan ini untuk menunjukkan ukuran file, siapa yang ingin memotong?) Jika Anda ingin kekuatan 2 daripada 10? Anda harus menulis ulang sedikit adil, bukan? Seperti yang saya katakan, saya sengaja tidak mencoba kode golf saya, banyak yang bisa dipersingkat (saya tidak akan pernah menyimpan if-then pada satu baris, misalnya).ini kode saya. bersih dan sederhana.
sumber
Menambahkan jawaban saya sendiri, kode Java, kode penjelasan sendiri ..
sumber
Cuplikan kode ini sangat sederhana, dan bersih, dan benar-benar berfungsi:
sumber
coba ini :
sumber
Keluaran:
sumber
sumber