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.mathnotjava.lang.Math—kotlin.mathworks on all Kotlin platforms (JVM, JS, Native). The functions are identical but available as top-level functions that can be imported directly.BigDecimalfor money and finance —Doublecan’t represent all decimals exactly. Always useBigDecimal("0.1")(from a String) rather thanBigDecimal(0.1)(from a Double).compareTo(other) == 0not==for BigDecimal —BigDecimal("1.0") != BigDecimal("1.00")because the scales differ. UsecompareTo()to compare values.coerceIn(min, max)— the idiomatic way to clamp a value within a range. Cleaner thanif (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 thansqrt(x*x + y*y)because it avoids overflow for very large values.expm1()andln1p()— for values close to zero, these functions are more precise thanexp(x) - 1andln(1 + x). Useful for small interest rate calculations.floorDiv()andmod()— for modulo that always produces a non-negative result (useful for circular indices), usemod()instead of the%operator.