Angka di program CLI sering perlu ditampilkan dengan format tertentu — pemisah ribuan agar mudah dibaca, padding dengan nol untuk ID berstruktur, tanda plus eksplisit untuk perbedaan positif/negatif, atau presisi desimal yang konsisten. Kotlin mewarisi seluruh mekanisme ini dari java.util.Formatter melalui String.format(). Pada chapter ini kita akan membahas flag-flag yang paling relevan untuk angka: koma (grup), nol (padding), plus (tanda positif), dan kombinasi precision dengan width.

Pemisah Grup dengan Flag Koma #

Flag koma , menambahkan pemisah grup sesuai locale default JVM:

kotlin Contoh: flag koma
val besar = 1234567.89
println("Tanpa grup: ${"%.2f".format(besar)}")   // 1234567.89
println("Dengan grup: ${"%,.2f".format(besar)}") // 1,234,567.89

Pemisah grup default di locale US adalah koma , untuk ribuan dan titik . untuk desimal. Di locale Jerman, perannya tertukar: titik untuk ribuan dan koma untuk desimal. Topik locale akan kita bahas tuntas pada chapter Locale dan Format Angka.

Padding dengan Nol #

Flag 0 diikuti width memaksa padding dengan karakter nol di depan angka:

kotlin Contoh: padding nol
val kecil = 42
println("%5d".format(kecil))   // "   42" (spasi)
println("%05d".format(kecil))  // "00042" (nol)
println("%08d".format(kecil))  // "00000042" (lebih lebar)

Padding dengan nol berguna untuk ID berstruktur seperti nomor pesanan (ORD-00042) atau kode pos (00123). Tanpa flag 0, padding default menggunakan spasi yang sulit terlihat di output.

Contoh Numeric Formatting #

kotlin
fun main() {
    val besar = 1234567.89
    println("Tanpa grup: ${"%.2f".format(besar)}")
    println("Dengan grup: ${"%,.2f".format(besar)}")

    val kecil = 42
    println("Pad spasi: ${"%5d".format(kecil)}")
    println("Pad nol: ${"%05d".format(kecil)}")
    println("Pad nol lebar 8: ${"%08d".format(kecil)}")

    val positif = 3.14
    val negatif = -3.14
    println("Positif: ${"%+.2f".format(positif)}")
    println("Negatif: ${"%+.2f".format(negatif)}")
    println("Tanpa plus positif: ${"%.2f".format(positif)}")

    val persen = 0.875
    println("Persentase: ${"%.1f%%".format(persen * 100)}")

    val hex = 255
    println("Pad hex: ${"%08x".format(hex)}")
    println("Hex upper: ${"%08X".format(hex)}")

    val pi = 3.141592653589793
    println("Pi 2 desimal: ${"%.2f".format(pi)}")
    println("Pi 4 desimal: ${"%.4f".format(pi)}")
    println("Pi 8 desimal: ${"%.8f".format(pi)}")

    val tabel = listOf(
        Triple("Apel", 10, 15.5),
        Triple("Jeruk", 3, 4.25),
        Triple("Mangga", 100, 250.0)
    )
    println("--- Tabel Buah ---")
    println("%-10s %5s %10s".format("Nama", "Jumlah", "Harga"))
    for ((nama, jumlah, harga) in tabel) {
        println("%-10s %5d %10.2f".format(nama, jumlah, harga))
    }
}

Output yang Dihasilkan:

text
Tanpa grup: 1234567.89
Dengan grup: 1,234,567.89
Pad spasi:    42
Pad nol: 00042
Pad nol lebar 8: 00000042
Positif: +3.14
Negatif: -3.14
Tanpa plus positif: 3.14
Persentase: 87.5%
Pad hex: 000000ff
Hex upper: 000000FF
Pi 2 desimal: 3.14
Pi 4 desimal: 3.1416
Pi 8 desimal: 3.14159265
--- Tabel Buah ---
Nama       Jumlah      Harga
Apel          10      15.50
Jeruk          3       4.25
Mangga       100     250.00

Mari kita telaah baris-baris kode di atas:

  1. Baris 3 ("%.2f".format(besar)): specifier %f dengan precision .2 menghasilkan 2 angka di belakang koma. Tanpa flag grup, output adalah 1234567.89 tanpa pemisah.
  2. Baris 4 ("%,.2f"): penambahan flag , memicu pemisah grup. Angka 1234567.89 ditampilkan sebagai 1,234,567.89. Karakter pemisah tergantung locale default.
  3. Baris 7 ("%5d"): width 5 menghasilkan field selebar 5 karakter. Angka 42 di-padding dengan 3 spasi di depan karena rata kanan secara default.
  4. Baris 8 ("%05d"): flag 0 mengubah padding menjadi nol. Output 00042. Flag 0 hanya berlaku untuk angka, tidak untuk %s atau %c.
  5. Baris 11 ("%+.2f".format(positif)): flag + memaksa tanda plus tampil untuk angka positif. Tanpa flag ini, angka positif dicetak tanpa tanda. Untuk angka negatif, tanda minus tetap muncul otomatis.
  6. Baris 17 ("%.1f%%".format(persen * 100)): kita kalikan 0.875 dengan 100 menjadi 87.5, lalu format dengan satu desimal. Specifier %% menghasilkan tanda persen literal.
  7. Baris 20–21 ("%08x" dan "%08X"): bilangan bulat 255 diformat dalam heksadesimal 8 digit dengan padding nol. Specifier lowercase x menghasilkan 000000ff, sedangkan uppercase X menghasilkan 000000FF. Pola ini umum untuk kode warna HTML atau hash.
  8. Baris 24–26 ("%.2f", "%.4f", "%.8f"): precision mengontrol jumlah desimal. Nilai pi dipotong/dibulatkan sesuai precision. Perhatikan Pi 4 desimal: 3.1416 — angka ke-5 (9) membulatkan angka ke-4 dari 5 menjadi 6.
  9. Baris 34 ("%-10s %5d %10.2f"): gabungan alignment dan width. %-10s rata kiri lebar 10, %5d rata kanan lebar 5, %10.2f rata kanan lebar 10 dengan 2 desimal. Hasilnya kolom yang rapi meskipun data memiliki panjang berbeda.

Rangkuman Flag untuk Angka #

FlagSpecifier contohEfek
,%,.2fPemisah grup (locale-dependent)
0%08dPadding dengan nol
+%+.2fTampilkan tanda + untuk positif
(%(,.2fBungkus negatif dalam kurung
-%-8dRata kiri
(spasi)% dSpasi untuk positif (menggantikan +)

Flag dapat dikombinasikan: %(,.2f sekaligus menggunakan tanda kurung untuk negatif dan pemisah grup. Output untuk -1234.5 dengan format tersebut adalah (1,234.50) — konvensi akuntansi.

Tabel Numerik Rapi #

Untuk tabel yang rapi, pola umum adalah menyimpan format di variabel dan memanggil berulang:

kotlin Contoh: pola tabel rapi
val baris = "%-10s %5d %10.2f"
println(baris.format("Apel", 10, 15.5))
println(baris.format("Jeruk", 3, 4.25))
println(baris.format("Mangga", 100, 250.0))

Karena %-10s selalu menghasilkan field selebar 10 karakter (rata kiri), kolom pertama tetap sejajar meskipun nama buah memiliki panjang berbeda. Pola yang sama untuk kolom numerik dengan %5d dan %10.2f.

Referensi Lanjutan #

Dengan pemahaman tentang flag-flag numerik, kita siap beranjak ke topik yang erat kaitannya: pengaruh locale terhadap pemisah grup, simbol desimal, dan format mata uang.