Math #

Kotlin provides mathematical functions from two sources: kotlin.math (a standard Kotlin package usable on all platforms — JVM, JS, Native) and java.lang.Math (JVM only, but Kotlin already wraps most of its functions). For code that needs to run on Kotlin Multiplatform, always use kotlin.math. This article is a comprehensive reference to all available mathematical functions, including constants, rounding, exponents, logarithms, trigonometry, random numbers, and BigDecimal for precise financial calculations.


Imports and Constants #

import kotlin.math.*  // import all kotlin.math functions and constants

// Mathematical constants
println(PI)       // 3.141592653589793 — π (pi)
println(E)        // 2.718281828459045 — Euler's number
println(sqrt(2.0)) // 1.4142135623730951 — square root of 2

// Constants from numeric types
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 (the smallest positive, not negative!)
println(Double.POSITIVE_INFINITY)  // Infinity
println(Double.NEGATIVE_INFINITY)  // -Infinity
println(Double.NaN)        // NaN (Not a Number)

// Check special values
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

Absolute Value and Sign #

// abs() — absolute value
println(abs(-5))      // 5
println(abs(-3.14))   // 3.14
println(abs(0))       // 0
println(abs(Int.MIN_VALUE))  // -2147483648 ← overflow! use Long
println(abs(Int.MIN_VALUE.toLong()))  // 2147483648

// absoluteValue — an extension property (more idiomatic)
println((-42).absoluteValue)      // 42
println((-3.14).absoluteValue)    // 3.14
println((-42L).absoluteValue)     // 42L

// sign() — the value's sign (-1, 0, or 1)
println(sign(-5.0))   // -1.0
println(sign(0.0))    // 0.0
println(sign(3.14))   // 1.0

// sign as a property
println((-42).sign)   // -1
println(0.sign)       // 0
println(100.sign)     // 1

// withSign() — apply the sign of another number
println(5.0.withSign(-1.0))   // -5.0
println((-3.0).withSign(1.0)) // 3.0

Rounding #

// round() — round to the nearest integer (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 from a negative value)

// roundToInt() — convert to Int after rounding
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() — round up (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 (toward zero for negatives!)
println(ceil(-3.9))  // -3.0

// floor() — round down (floor)
println(floor(3.1))  // 3.0
println(floor(3.9))  // 3.0
println(floor(-3.1)) // -4.0 (away from zero for negatives)
println(floor(-3.9)) // -4.0

// truncate() — cut off the decimal part (toward zero)
println(truncate(3.9))   // 3.0
println(truncate(-3.9))  // -3.0 (different from floor!)

// Rounding to N decimal places with 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 — away from zero)

Exponents and Roots #

// pow() — power
println(2.0.pow(10))       // 1024.0
println(3.0.pow(3))        // 27.0
println(4.0.pow(0.5))      // 2.0 (square root)
println(8.0.pow(1.0/3.0))  // 2.0 (cube root)

// sqrt() — square root
println(sqrt(16.0))  // 4.0
println(sqrt(2.0))   // 1.4142135623730951
println(sqrt(-1.0))  // NaN (square root of a negative)

// 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, more precise for small values
println(expm1(0.0001))    // 1.0000500016667084E-4 (more precise than exp(0.0001) - 1)

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

// Euclidean distance between two points
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

Logarithms #

// ln() — natural logarithm (base e)
println(ln(1.0))   // 0.0
println(ln(E))     // 1.0
println(ln(10.0))  // 2.302585092994046

// log2() — base 2 logarithm
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() — base 10 logarithm
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() — logarithm with an arbitrary base
println(log(8.0, 2.0))   // 3.0 (base 2 log of 8)
println(log(27.0, 3.0))  // 3.0 (base 3 log of 27)

// ln1p() — ln(1 + x), more precise for small x
println(ln1p(0.0001))   // 9.999500033330833E-5 (more precise than ln(1 + 0.0001))

// Example: calculating the growth rate
fun tingkatPertumbuhan(awal: Double, akhir: Double, tahun: Int): Double {
    return (exp(ln(akhir / awal) / tahun) - 1) * 100  // CAGR in percent
}
println("%.2f%%".format(tingkatPertumbuhan(100.0, 200.0, 7)))  // 10.41% CAGR

Minimum and Maximum Values #

// max() and min() — two values
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() and minOf() — can take more than two values
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 with a selector (for objects)
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("Most expensive: ${termahal?.nama}")  // Laptop
println("Cheapest: ${termurah?.nama}")  // Mouse

// coerceIn — clamp a value within a range
println((-5).coerceIn(0, 100))    // 0 (below minimum, use the minimum)
println(150.coerceIn(0, 100))     // 100 (above maximum, use the maximum)
println(42.coerceIn(0, 100))      // 42 (within the range, use as-is)

println(3.14.coerceIn(0.0, 1.0))  // 1.0 (above max)
println(0.5.coerceIn(0.0, 1.0))   // 0.5 (within the range)

// coerceAtLeast and coerceAtMost
println((-5).coerceAtLeast(0))   // 0 (minimum 0)
println(150.coerceAtMost(100))   // 100 (maximum 100)

Trigonometry #

import kotlin.math.*

// Converting degrees ↔ radians
fun Double.toRad() = this * PI / 180.0
fun Double.toDeg() = this * 180.0 / PI

// sin, cos, tan — in radians
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°)

// In degrees
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 → degrees)
println(acos(0.5).toDeg())    // 60.0
println(atan(1.0).toDeg())    // 45.0

// atan2 — the angle from the x-axis to the point (y, x)
println(atan2(1.0, 1.0).toDeg())   // 45.0 (point (1,1) = 45°)
println(atan2(1.0, 0.0).toDeg())   // 90.0 (point (0,1) = 90°)
println(atan2(-1.0, -1.0).toDeg()) // -135.0 (point (-1,-1))

// sinh, cosh, tanh — hyperbolic functions
println(sinh(0.0))  // 0.0
println(cosh(0.0))  // 1.0
println(tanh(0.0))  // 0.0

// Example: calculating the distance on the Earth's surface (Haversine formula)
fun jarakBumi(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
    val R = 6371.0  // Earth's radius in 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) to Surabaya (-7.2, 112.7)
println("%.0f km".format(jarakBumi(-6.2, 106.8, -7.2, 112.7)))  // ~699 km

Random Numbers #

import kotlin.random.Random

// Basic Random
val acak = Random.nextInt()           // any random Int
val acakRange = Random.nextInt(100)   // 0 to 99
val acakBatas = Random.nextInt(10, 50)  // 10 to 49

println(Random.nextLong())            // random Long
println(Random.nextDouble())          // Double between 0.0 and 1.0
println(Random.nextDouble(0.0, 10.0)) // Double between 0.0 and 10.0
println(Random.nextFloat())           // Float between 0.0 and 1.0
println(Random.nextBoolean())         // true or false

// Random with a seed (reproducible — useful for testing)
val seed = 42L
val r1 = Random(seed)
val r2 = Random(seed)
println(r1.nextInt(100))  // always the same
println(r2.nextInt(100))  // always the same as r1

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

val immutable = listOf("A", "B", "C", "D", "E")
val diacak = immutable.shuffled()           // return a new shuffled list
val diacakDenganSeed = immutable.shuffled(Random(42))

// Sample — take N random elements
val sampel = immutable.shuffled().take(3)
println(sampel)  // 3 random elements from the list

// random() — take one random element from a collection
val pilihan = daftar.random()
println(pilihan)

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

BigDecimal — Precise Arithmetic for Finance #

Don’t use Double for financial calculations — use BigDecimal:

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

// ANTI-PATTERN: financial calculations with Double
println(0.1 + 0.2)           // 0.30000000000000004 ← not 0.3!
println(1.0 - 0.9)           // 0.09999999999999998 ← not 0.1!
println(0.1 * 3)             // 0.30000000000000004

// CORRECT: use BigDecimal
val a = BigDecimal("0.1")    // MUST come from a String, not from a Double!
val b = BigDecimal("0.2")
println(a + b)               // 0.3

// ANTI-PATTERN: BigDecimal from a Double
println(BigDecimal(0.1))     // 0.1000000000000000055511151231257827021181583404541015625 ← wrong!
println(BigDecimal("0.1"))   // 0.1 ← correct!

// Arithmetic operations
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

// Division with precision (MUST specify a scale for division)
val pembagian = BigDecimal("10") / BigDecimal("3")   // ArithmeticException! (non-terminating)
// Must specify the scale and rounding mode:
val dibagi = BigDecimal("10").divide(BigDecimal("3"), 2, RoundingMode.HALF_UP)
println(dibagi)  // 3.33

// Scale and rounding modes
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

// Comparing BigDecimals — don't use == because it considers scale
val x = BigDecimal("1.00")
val y = BigDecimal("1.0")
println(x == y)            // FALSE! different scales (2 vs 1)
println(x.compareTo(y))    // 0 ← this is the right way to check value equality
println(x.compareTo(y) == 0)  // true

// Conversion
println(BigDecimal("42.5").toInt())     // 42 (truncates the decimal)
println(BigDecimal("42.5").toLong())    // 42
println(BigDecimal("42.5").toDouble()) // 42.5

// Convenience extension functions
fun Double.toBigDecimalAman(): BigDecimal = toString().toBigDecimal()
fun Int.toBigDecimal(): BigDecimal = BigDecimal(this)

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

Extension Functions on Numeric Types #

Kotlin adds many useful functions directly on numeric types:

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

// Conversion functions
println(3.toDouble())            // 3.0
println(3.14.toInt())            // 3 (truncates)
println(3.14.roundToInt())       // 3

// Integer division with floor (for always-positive modulo)
println(7.floorDiv(3))           // 2
println((-7).floorDiv(3))        // -3 (different from -7/3 = -2!)
println((-7).mod(3))             // 2 (always positive)
println(-7 % 3)                  // -1 (the sign follows the dividend)

// GCD and LCM for integers
// Not available directly, but easy to create
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

// Check whether a number is within a range
println(5 in 1..10)     // true
println(15 in 1..10)    // false

// sum and average on numeric collections
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 (sum of squares)

// Statistics on collections
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

Practical Application Examples #

Calculating Compound Interest #

fun hitungBungaMajemuk(
    modalAwal: Double,
    sukuBungaTahunan: Double,  // as a decimal, e.g., 0.05 for 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("Principal: Rp${"%,.0f".format(modal)}")
println("After 10 years: Rp${"%,.0f".format(hasil)}")
// Principal: Rp10,000,000
// After 10 years: Rp16,470,095

Data Normalization (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]

Basic Statistical Calculations #

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("Mean: ${data.average()}")         // 5.0
println("Median: ${data.median()}")             // 4.5
println("Std Deviation: ${data.standarDeviasi()}") // 2.0
println("Min: ${data.min()}, Max: ${data.max()}") // 2.0, 9.0

Summary #

  • Use kotlin.math not java.lang.Mathkotlin.math works on all Kotlin platforms (JVM, JS, Native). The functions are identical but available as top-level functions that can be imported directly.
  • BigDecimal for money and financeDouble can’t represent all decimals exactly. Always use BigDecimal("0.1") (from a String) rather than BigDecimal(0.1) (from a Double).
  • compareTo(other) == 0 not == for BigDecimalBigDecimal("1.0") != BigDecimal("1.00") because the scales differ. Use compareTo() to compare values.
  • coerceIn(min, max) — the idiomatic way to clamp a value within a range. Cleaner than if (x < min) min else if (x > max) max else x.
  • Random(seed) for reproducible tests — give the same seed to get the same random sequence. Useful in tests involving random elements.
  • hypot(x, y) for distances — more precise than sqrt(x*x + y*y) because it avoids overflow for very large values.
  • expm1() and ln1p() — for values close to zero, these functions are more precise than exp(x) - 1 and ln(1 + x). Useful for small interest rate calculations.
  • floorDiv() and mod() — for modulo that always produces a non-negative result (useful for circular indices), use mod() instead of the % operator.

← Previous: IO   Next: Collections →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact