Math

Math #

Kotlin menyediakan fungsi matematika melalui dua sumber: kotlin.math (paket standar Kotlin yang bisa digunakan di semua platform — JVM, JS, Native) dan java.lang.Math (JVM saja, tapi Kotlin sudah membungkus sebagian besar fungsinya). Untuk kode yang perlu berjalan di Kotlin Multiplatform, selalu gunakan kotlin.math. Artikel ini adalah referensi komprehensif semua fungsi matematika yang tersedia, termasuk konstanta, pembulatan, eksponen, logaritma, trigonometri, bilangan acak, dan BigDecimal untuk perhitungan keuangan yang presisi.


Import dan Konstanta #

import kotlin.math.*  // import semua fungsi dan konstanta kotlin.math

// Konstanta matematika
println(PI)       // 3.141592653589793 — π (pi)
println(E)        // 2.718281828459045 — bilangan Euler
println(sqrt(2.0)) // 1.4142135623730951 — akar 2

// Konstanta dari tipe numerik
println(Int.MAX_VALUE)     // 2147483647
println(Int.MIN_VALUE)     // -2147483648
println(Long.MAX_VALUE)    // 9223372036854775807
println(Double.MAX_VALUE)  // 1.7976931348623157E308
println(Double.MIN_VALUE)  // 5.0E-324 (terkecil positif, bukan negatif!)
println(Double.POSITIVE_INFINITY)  // Infinity
println(Double.NEGATIVE_INFINITY)  // -Infinity
println(Double.NaN)        // NaN (Not a Number)

// Cek nilai khusus
println(Double.isNaN(Double.NaN))         // true
println(Double.isInfinite(1.0 / 0.0))    // true
println(1.0.isNaN())                       // false
println((1.0 / 0.0).isInfinite())         // true
println(42.0.isFinite())                   // true

Nilai Absolut dan Tanda #

// abs() — nilai absolut
println(abs(-5))      // 5
println(abs(-3.14))   // 3.14
println(abs(0))       // 0
println(abs(Int.MIN_VALUE))  // -2147483648 ← overflow! gunakan Long
println(abs(Int.MIN_VALUE.toLong()))  // 2147483648

// absoluteValue — properti extension (lebih idiomatis)
println((-42).absoluteValue)      // 42
println((-3.14).absoluteValue)    // 3.14
println((-42L).absoluteValue)     // 42L

// sign() — tanda nilai (-1, 0, atau 1)
println(sign(-5.0))   // -1.0
println(sign(0.0))    // 0.0
println(sign(3.14))   // 1.0

// sign sebagai properti
println((-42).sign)   // -1
println(0.sign)       // 0
println(100.sign)     // 1

// withSign() — terapkan tanda dari bilangan lain
println(5.0.withSign(-1.0))   // -5.0
println((-3.0).withSign(1.0)) // 3.0

Pembulatan #

// round() — bulatkan ke integer terdekat (half up)
println(round(3.4))   // 3.0
println(round(3.5))   // 4.0
println(round(3.6))   // 4.0
println(round(-3.5))  // -3.0 (half up dari nilai negatif)

// roundToInt() — konversi ke Int setelah round
println(3.7.roundToInt())    // 4
println(3.4.roundToInt())    // 3
println((-3.5).roundToInt()) // -3

// roundToLong()
println(3_000_000_000.7.roundToLong())  // 3000000001

// ceil() — bulatkan ke atas (ceiling)
println(ceil(3.1))   // 4.0
println(ceil(3.9))   // 4.0
println(ceil(3.0))   // 3.0
println(ceil(-3.1))  // -3.0 (arah mendekati nol untuk negatif!)
println(ceil(-3.9))  // -3.0

// floor() — bulatkan ke bawah (floor)
println(floor(3.1))  // 3.0
println(floor(3.9))  // 3.0
println(floor(-3.1)) // -4.0 (arah menjauhi nol untuk negatif)
println(floor(-3.9)) // -4.0

// truncate() — potong bagian desimal (menuju nol)
println(truncate(3.9))   // 3.0
println(truncate(-3.9))  // -3.0 (berbeda dari floor!)

// Pembulatan ke N desimal dengan BigDecimal
import java.math.BigDecimal
import java.math.RoundingMode

fun Double.bulatkan(desimal: Int, mode: RoundingMode = RoundingMode.HALF_UP): Double {
    return BigDecimal(this).setScale(desimal, mode).toDouble()
}

println(3.14159.bulatkan(2))   // 3.14
println(3.14559.bulatkan(2))   // 3.15
println(2.5.bulatkan(0))       // 3.0
println((-2.5).bulatkan(0))    // -3.0 (HALF_UP — arah menjauhi nol)

Eksponen dan Akar #

// pow() — pangkat
println(2.0.pow(10))       // 1024.0
println(3.0.pow(3))        // 27.0
println(4.0.pow(0.5))      // 2.0 (akar kuadrat)
println(8.0.pow(1.0/3.0))  // 2.0 (akar kubik)

// sqrt() — akar kuadrat
println(sqrt(16.0))  // 4.0
println(sqrt(2.0))   // 1.4142135623730951
println(sqrt(-1.0))  // NaN (akar kuadrat dari negatif)

// exp() — e^x
println(exp(0.0))   // 1.0
println(exp(1.0))   // 2.718281828459045 (= E)
println(exp(2.0))   // 7.38905609893065

// expm1() — e^x - 1, lebih presisi untuk nilai kecil
println(expm1(0.0001))    // 1.0000500016667084E-4 (lebih presisi dari exp(0.0001) - 1)

// hypot() — panjang hipotenusa: sqrt(x² + y²)
println(hypot(3.0, 4.0))  // 5.0
println(hypot(5.0, 12.0)) // 13.0

// Jarak Euclidean antara dua titik
fun jarakEuclidean(x1: Double, y1: Double, x2: Double, y2: Double): Double {
    return hypot(x2 - x1, y2 - y1)
}
println(jarakEuclidean(0.0, 0.0, 3.0, 4.0))  // 5.0

Logaritma #

// ln() — logaritma natural (basis e)
println(ln(1.0))   // 0.0
println(ln(E))     // 1.0
println(ln(10.0))  // 2.302585092994046

// log2() — logaritma basis 2
println(log2(1.0))    // 0.0
println(log2(2.0))    // 1.0
println(log2(8.0))    // 3.0
println(log2(1024.0)) // 10.0

// log10() — logaritma basis 10
println(log10(1.0))     // 0.0
println(log10(10.0))    // 1.0
println(log10(100.0))   // 2.0
println(log10(1000.0))  // 3.0

// log() — logaritma dengan basis sembarang
println(log(8.0, 2.0))   // 3.0 (log basis 2 dari 8)
println(log(27.0, 3.0))  // 3.0 (log basis 3 dari 27)

// ln1p() — ln(1 + x), lebih presisi untuk x kecil
println(ln1p(0.0001))   // 9.999500033330833E-5 (lebih presisi dari ln(1 + 0.0001))

// Contoh: menghitung tingkat pertumbuhan
fun tingkatPertumbuhan(awal: Double, akhir: Double, tahun: Int): Double {
    return (exp(ln(akhir / awal) / tahun) - 1) * 100  // CAGR dalam persen
}
println("%.2f%%".format(tingkatPertumbuhan(100.0, 200.0, 7)))  // 10.41% CAGR

Nilai Minimum dan Maksimum #

// max() dan min() — dua nilai
println(max(3, 7))          // 7
println(min(3, 7))          // 3
println(max(3.14, 2.71))    // 3.14
println(min(-5.0, -3.0))    // -5.0

// maxOf() dan minOf() — bisa lebih dari dua nilai
println(maxOf(3, 7, 1, 9, 2))          // 9
println(minOf(3, 7, 1, 9, 2))          // 1
println(maxOf(3.14, 2.71, 1.41, 1.73)) // 3.14

// maxOf dengan selector (untuk objek)
data class Produk(val nama: String, val harga: Double)
val produk = listOf(
    Produk("Laptop", 15_000_000.0),
    Produk("Mouse", 250_000.0),
    Produk("Monitor", 4_000_000.0)
)

val termahal = produk.maxByOrNull { it.harga }
val termurah = produk.minByOrNull { it.harga }
println("Termahal: ${termahal?.nama}")  // Laptop
println("Termurah: ${termurah?.nama}")  // Mouse

// coerceIn — batasi nilai dalam rentang
println((-5).coerceIn(0, 100))    // 0 (nilai di bawah minimum, gunakan minimum)
println(150.coerceIn(0, 100))     // 100 (nilai di atas maksimum, gunakan maksimum)
println(42.coerceIn(0, 100))      // 42 (dalam rentang, gunakan apa adanya)

println(3.14.coerceIn(0.0, 1.0))  // 1.0 (di atas maks)
println(0.5.coerceIn(0.0, 1.0))   // 0.5 (dalam rentang)

// coerceAtLeast dan coerceAtMost
println((-5).coerceAtLeast(0))   // 0 (minimal 0)
println(150.coerceAtMost(100))   // 100 (maksimal 100)

Trigonometri #

import kotlin.math.*

// Konversi derajat ↔ radian
fun Double.toRad() = this * PI / 180.0
fun Double.toDeg() = this * 180.0 / PI

// sin, cos, tan — dalam radian
println(sin(0.0))          // 0.0
println(sin(PI / 2))       // 1.0 (sin 90°)
println(cos(0.0))          // 1.0
println(cos(PI))           // -1.0 (cos 180°)
println(tan(PI / 4))       // 1.0 (tan 45°)

// Dalam derajat
println(sin(90.0.toRad()))   // 1.0
println(cos(60.0.toRad()))   // 0.5
println(tan(45.0.toRad()))   // 1.0

// arc functions (inverse trigonometry)
println(asin(1.0).toDeg())    // 90.0 (arcsin → derajat)
println(acos(0.5).toDeg())    // 60.0
println(atan(1.0).toDeg())    // 45.0

// atan2 — sudut dari sumbu x ke titik (y, x)
println(atan2(1.0, 1.0).toDeg())   // 45.0 (titik (1,1) = 45°)
println(atan2(1.0, 0.0).toDeg())   // 90.0 (titik (0,1) = 90°)
println(atan2(-1.0, -1.0).toDeg()) // -135.0 (titik (-1,-1))

// sinh, cosh, tanh — fungsi hiperbolik
println(sinh(0.0))  // 0.0
println(cosh(0.0))  // 1.0
println(tanh(0.0))  // 0.0

// Contoh: hitung jarak di permukaan bumi (Haversine formula)
fun jarakBumi(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
    val R = 6371.0  // radius bumi dalam km
    val dLat = (lat2 - lat1).toRad()
    val dLon = (lon2 - lon1).toRad()
    val a = sin(dLat / 2).pow(2) +
            cos(lat1.toRad()) * cos(lat2.toRad()) * sin(dLon / 2).pow(2)
    return R * 2 * atan2(sqrt(a), sqrt(1 - a))
}

// Jakarta (-6.2, 106.8) ke Surabaya (-7.2, 112.7)
println("%.0f km".format(jarakBumi(-6.2, 106.8, -7.2, 112.7)))  // ~699 km

Bilangan Acak #

import kotlin.random.Random

// Random dasar
val acak = Random.nextInt()           // Int acak apapun
val acakRange = Random.nextInt(100)   // 0 sampai 99
val acakBatas = Random.nextInt(10, 50)  // 10 sampai 49

println(Random.nextLong())            // Long acak
println(Random.nextDouble())          // Double antara 0.0 dan 1.0
println(Random.nextDouble(0.0, 10.0)) // Double antara 0.0 dan 10.0
println(Random.nextFloat())           // Float antara 0.0 dan 1.0
println(Random.nextBoolean())         // true atau false

// Random dengan seed (reprodusibel — berguna untuk testing)
val seed = 42L
val r1 = Random(seed)
val r2 = Random(seed)
println(r1.nextInt(100))  // selalu sama
println(r2.nextInt(100))  // selalu sama dengan r1

// Shuffle dan sample
val daftar = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
daftar.shuffle()            // acak urutan in-place
println(daftar)

val immutable = listOf("A", "B", "C", "D", "E")
val diacak = immutable.shuffled()           // kembalikan list baru yang diacak
val diacakDenganSeed = immutable.shuffled(Random(42))

// Sample — ambil N elemen acak
val sampel = immutable.shuffled().take(3)
println(sampel)  // 3 elemen acak dari list

// random() — ambil satu elemen acak dari koleksi
val pilihan = daftar.random()
println(pilihan)

val pilihanDenganSeed = daftar.random(Random(42))

BigDecimal — Aritmatika Presisi untuk Keuangan #

Jangan gunakan Double untuk perhitungan keuangan — gunakan BigDecimal:

import java.math.BigDecimal
import java.math.RoundingMode
import java.math.MathContext

// ANTI-PATTERN: perhitungan keuangan dengan Double
println(0.1 + 0.2)           // 0.30000000000000004 ← bukan 0.3!
println(1.0 - 0.9)           // 0.09999999999999998 ← bukan 0.1!
println(0.1 * 3)             // 0.30000000000000004

// BENAR: gunakan BigDecimal
val a = BigDecimal("0.1")    // HARUS dari String, bukan dari Double!
val b = BigDecimal("0.2")
println(a + b)               // 0.3

// ANTI-PATTERN: BigDecimal dari Double
println(BigDecimal(0.1))     // 0.1000000000000000055511151231257827021181583404541015625 ← salah!
println(BigDecimal("0.1"))   // 0.1 ← benar!

// Operasi aritmatika
val harga = BigDecimal("15000000")
val persen = BigDecimal("0.10")
val diskon = harga * persen                          // operator overloading
println(diskon)  // 1500000.0

val hargaAkhir = harga - diskon
println(hargaAkhir)  // 13500000.0

// Pembagian dengan presisi (WAJIB tentukan scale untuk pembagian)
val pembagian = BigDecimal("10") / BigDecimal("3")   // ArithmeticException! (tak terbatas)
// Harus tentukan scale dan rounding mode:
val dibagi = BigDecimal("10").divide(BigDecimal("3"), 2, RoundingMode.HALF_UP)
println(dibagi)  // 3.33

// Scale dan rounding mode
val nilai = BigDecimal("123.4567")
println(nilai.setScale(2, RoundingMode.HALF_UP))     // 123.46
println(nilai.setScale(2, RoundingMode.FLOOR))       // 123.45
println(nilai.setScale(2, RoundingMode.CEILING))     // 123.46
println(nilai.setScale(0, RoundingMode.HALF_UP))     // 123

// Perbandingan BigDecimal — jangan gunakan == karena mempertimbangkan scale
val x = BigDecimal("1.00")
val y = BigDecimal("1.0")
println(x == y)            // FALSE! scale berbeda (2 vs 1)
println(x.compareTo(y))    // 0 ← ini yang benar untuk cek kesamaan nilai
println(x.compareTo(y) == 0)  // true

// Konversi
println(BigDecimal("42.5").toInt())     // 42 (potong desimal)
println(BigDecimal("42.5").toLong())    // 42
println(BigDecimal("42.5").toDouble()) // 42.5

// Extension function untuk kemudahan
fun Double.toBigDecimalAman(): BigDecimal = toString().toBigDecimal()
fun Int.toBigDecimal(): BigDecimal = BigDecimal(this)

println(1.5.toBigDecimalAman() + 2.5.toBigDecimalAman())  // 4.0

Fungsi Extension pada Tipe Numerik #

Kotlin menambahkan banyak fungsi berguna langsung pada tipe numerik:

// Extension property
println((-42).absoluteValue)     // 42
println(3.14.absoluteValue)      // 3.14
println((-3).sign)               // -1

// Fungsi konversi
println(3.toDouble())            // 3.0
println(3.14.toInt())            // 3 (truncate)
println(3.14.roundToInt())       // 3

// Integer division dengan floor (untuk modulo yang selalu positif)
println(7.floorDiv(3))           // 2
println((-7).floorDiv(3))        // -3 (berbeda dari -7/3 = -2!)
println((-7).mod(3))             // 2 (selalu positif)
println(-7 % 3)                  // -1 (tanda mengikuti dividend)

// GCD dan LCM untuk Integer
// Tidak tersedia langsung, tapi mudah dibuat
fun Int.gcd(other: Int): Int {
    var a = abs(this)
    var b = abs(other)
    while (b != 0) { val t = b; b = a % b; a = t }
    return a
}
fun Int.lcm(other: Int): Int = abs(this / gcd(other) * other)

println(12.gcd(8))   // 4
println(12.lcm(8))   // 24

// Cek apakah bilangan dalam rentang
println(5 in 1..10)     // true
println(15 in 1..10)    // false

// sum dan average pada koleksi numerik
val angka = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
println(angka.sum())              // 55
println(angka.average())          // 5.5
println(angka.sumOf { it * it })  // 385 (jumlah kuadrat)

// Statistik pada koleksi
println(angka.min())   // 1
println(angka.max())   // 10

val doubles = listOf(1.5, 2.5, 3.5, 4.5)
println(doubles.sum())      // 12.0
println(doubles.average())  // 3.0

Contoh Penerapan Praktis #

Menghitung Bunga Majemuk #

fun hitungBungaMajemuk(
    modalAwal: Double,
    sukuBungaTahunan: Double,  // dalam desimal, mis: 0.05 untuk 5%
    frekuensiPerTahun: Int,
    tahun: Int
): Double {
    return modalAwal * (1 + sukuBungaTahunan / frekuensiPerTahun)
        .pow(frekuensiPerTahun * tahun.toDouble())
}

val modal = 10_000_000.0
val hasil = hitungBungaMajemuk(modal, 0.05, 12, 10)
println("Modal: Rp${"%,.0f".format(modal)}")
println("Setelah 10 tahun: Rp${"%,.0f".format(hasil)}")
// Modal: Rp10,000,000
// Setelah 10 tahun: Rp16,470,095

Normalisasi Data (Min-Max Scaling) #

fun List<Double>.normalisasiMinMax(): List<Double> {
    val min = minOrNull() ?: return this
    val max = maxOrNull() ?: return this
    if (max == min) return map { 0.0 }
    return map { (it - min) / (max - min) }
}

val data = listOf(10.0, 20.0, 30.0, 40.0, 50.0)
val ternormalisasi = data.normalisasiMinMax()
println(ternormalisasi)  // [0.0, 0.25, 0.5, 0.75, 1.0]

Perhitungan Statistik Dasar #

fun List<Double>.standarDeviasi(): Double {
    if (size <= 1) return 0.0
    val rata = average()
    val variansi = sumOf { (it - rata).pow(2) } / size
    return sqrt(variansi)
}

fun List<Double>.median(): Double {
    val terurut = sorted()
    return if (size % 2 == 0) {
        (terurut[size / 2 - 1] + terurut[size / 2]) / 2.0
    } else {
        terurut[size / 2]
    }
}

val data = listOf(2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0)
println("Rata-rata: ${data.average()}")         // 5.0
println("Median: ${data.median()}")             // 4.5
println("Std Deviasi: ${data.standarDeviasi()}") // 2.0
println("Min: ${data.min()}, Max: ${data.max()}") // 2.0, 9.0

Ringkasan #

  • Gunakan kotlin.math bukan java.lang.Mathkotlin.math bekerja di semua platform Kotlin (JVM, JS, Native). Fungsinya identik tapi tersedia sebagai top-level function yang bisa diimport langsung.
  • BigDecimal untuk uang dan keuanganDouble tidak bisa merepresentasikan semua desimal secara tepat. Selalu gunakan BigDecimal("0.1") (dari String) bukan BigDecimal(0.1) (dari Double).
  • compareTo(other) == 0 bukan == untuk BigDecimalBigDecimal("1.0") != BigDecimal("1.00") karena scale berbeda. Gunakan compareTo() untuk membandingkan nilai.
  • coerceIn(min, max) — cara idiomatis membatasi nilai dalam rentang. Lebih bersih dari if (x < min) min else if (x > max) max else x.
  • Random(seed) untuk test yang reprodusibel — berikan seed yang sama untuk mendapat urutan acak yang sama. Berguna dalam test yang melibatkan elemen acak.
  • hypot(x, y) untuk jarak — lebih presisi dari sqrt(x*x + y*y) karena menghindari overflow untuk nilai sangat besar.
  • expm1() dan ln1p() — untuk nilai mendekati nol, fungsi ini lebih presisi dari exp(x) - 1 dan ln(1 + x). Berguna untuk perhitungan suku bunga kecil.
  • floorDiv() dan mod() — untuk modulo yang selalu menghasilkan nilai non-negatif (berguna untuk indeks melingkar), gunakan mod() bukan operator %.

← Sebelumnya: IO
About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact