Math #

Komputasi numerik ada di mana-mana — menghitung diskon, menghasilkan angka acak, memvalidasi koordinat geografis, memproses data statistik, hingga mengimplementasikan algoritma kriptografi. Go menyediakan package math di standard library yang mencakup semua kebutuhan matematika umum: konstanta penting, fungsi trigonometri, logaritma, eksponen, pembulatan, dan nilai batas tipe numerik. Di luar itu, Go juga menyediakan math/rand untuk bilangan acak dan math/big untuk aritmetika presisi arbitrer ketika tipe float64 dan int64 tidak lagi cukup. Artikel ini membahas keseluruhan ekosistem package math di Go — cara kerjanya, kapan menggunakannya, dan jebakan numerik yang perlu diwaspadai.

Konstanta Matematika #

Package math mendefinisikan konstanta-konstanta penting yang sering dibutuhkan dalam komputasi ilmiah dan engineering. Semua konstanta ini bertipe float64 dengan presisi penuh.

import (
    "fmt"
    "math"
)

func main() {
    fmt.Println(math.Pi)      // 3.141592653589793   — π
    fmt.Println(math.E)       // 2.718281828459045   — bilangan Euler
    fmt.Println(math.Phi)     // 1.618033988749895   — golden ratio φ
    fmt.Println(math.Sqrt2)   // 1.4142135623730951  — √2
    fmt.Println(math.SqrtE)   // 1.6487212707001282  — √e
    fmt.Println(math.Log2E)   // 1.4426950408889634  — log₂(e)
    fmt.Println(math.Log10E)  // 0.4342944819032518  — log₁₀(e)
    fmt.Println(math.Ln2)     // 0.6931471805599453  — ln(2)
}

Selain konstanta matematika, math juga mendefinisikan nilai batas untuk tipe numerik Go:

// Nilai maksimum dan minimum float64
fmt.Println(math.MaxFloat64)    // 1.7976931348623157e+308
fmt.Println(math.SmallestNonzeroFloat64) // 5e-324

// Nilai maksimum integer (berguna untuk algoritma pencarian minimum)
fmt.Println(math.MaxInt8)   // 127
fmt.Println(math.MaxInt16)  // 32767
fmt.Println(math.MaxInt32)  // 2147483647
fmt.Println(math.MaxInt64)  // 9223372036854775807
fmt.Println(math.MinInt64)  // -9223372036854775808

fmt.Println(math.MaxFloat32) // 3.4028234663852886e+38

Nilai Spesial: Inf dan NaN #

math mendukung nilai-nilai spesial IEEE 754 yang perlu kamu pahami saat bekerja dengan float64:

// Infinity — hasil dari operasi yang melampaui batas float64
posInf := math.Inf(1)   // +∞
negInf := math.Inf(-1)  // -∞

fmt.Println(math.IsInf(posInf, 1))  // true
fmt.Println(math.IsInf(negInf, -1)) // true
fmt.Println(1.0 / 0.0)              // compile error — Go tidak izinkan ini
fmt.Println(math.Log(0))            // -Inf

// NaN — Not a Number, hasil operasi tidak terdefinisi
nan := math.NaN()
fmt.Println(math.IsNaN(nan))        // true
fmt.Println(nan == nan)             // false! NaN tidak sama dengan dirinya sendiri
fmt.Println(math.Sqrt(-1))          // NaN

// ANTI-PATTERN: membandingkan float64 dengan ==
var x float64 = math.Sqrt(-1)
if x == math.NaN() {      // selalu false, bahkan jika x adalah NaN
    fmt.Println("ini NaN")
}

// BENAR: gunakan math.IsNaN
if math.IsNaN(x) {
    fmt.Println("ini NaN")
}

Pembulatan: Floor, Ceil, Round, dan Trunc #

Empat fungsi pembulatan ini punya perilaku yang berbeda dan sering membingungkan jika tidak dipahami dengan baik.

FungsiPerilakuContoh (+2.7)Contoh (-2.7)
math.Floor(x)Bulatkan ke bawah (lantai)2-3
math.Ceil(x)Bulatkan ke atas (langit-langit)3-2
math.Round(x)Bulatkan ke terdekat (half away from zero)3-3
math.Trunc(x)Potong desimal (arah nol)2-2
x := 2.7
y := -2.7

fmt.Println(math.Floor(x), math.Floor(y))   // 2  -3
fmt.Println(math.Ceil(x), math.Ceil(y))     // 3  -2
fmt.Println(math.Round(x), math.Round(y))   // 3  -3
fmt.Println(math.Trunc(x), math.Trunc(y))   // 2  -2

// Mod — sisa bagi (modulo) untuk float64
fmt.Println(math.Mod(10.5, 3.0)) // 1.5
fmt.Println(math.Mod(-10.5, 3.0)) // -1.5

// Modf — pisahkan bagian integer dan desimal
integer, desimal := math.Modf(3.75)
fmt.Println(integer, desimal) // 3  0.75

integer, desimal = math.Modf(-3.75)
fmt.Println(integer, desimal) // -3  -0.75

Pembulatan ke N Desimal #

math tidak menyediakan fungsi untuk membulatkan ke N angka desimal secara langsung, tapi pola ini sangat umum di aplikasi:

// Bulatkan ke 2 angka desimal
func bulatkan(x float64, desimal int) float64 {
    factor := math.Pow(10, float64(desimal))
    return math.Round(x*factor) / factor
}

fmt.Println(bulatkan(3.14159, 2)) // 3.14
fmt.Println(bulatkan(2.675, 2))   // 2.68
fmt.Println(bulatkan(1.005, 2))   // mungkin tidak 1.01 karena floating point!
Pembulatan uang tidak boleh menggunakan float64. Representasi biner float64 tidak bisa merepresentasikan semua desimal dengan tepat — misalnya 0.1 + 0.2 menghasilkan 0.30000000000000004, bukan 0.3. Untuk kalkulasi finansial, gunakan integer (sen/sen terkecil) atau package math/big dengan big.Rat / big.Float yang menawarkan presisi eksak.

Akar dan Pangkat #

Sqrt, Cbrt, dan Pow #

// Akar kuadrat
fmt.Println(math.Sqrt(16))   // 4
fmt.Println(math.Sqrt(2))    // 1.4142135623730951
fmt.Println(math.Sqrt(-1))   // NaN — akar dari negatif tidak real

// Akar kubik
fmt.Println(math.Cbrt(27))   // 3
fmt.Println(math.Cbrt(-8))   // -2 (berbeda dari Sqrt, Cbrt mendukung negatif)

// Pangkat — Pow(x, y) = x^y
fmt.Println(math.Pow(2, 10))  // 1024
fmt.Println(math.Pow(3, 3))   // 27
fmt.Println(math.Pow(4, 0.5)) // 2 (sama dengan Sqrt(4))
fmt.Println(math.Pow(2, -1))  // 0.5

// Pow10 — pangkat 10 yang lebih efisien dari Pow(10, n)
fmt.Println(math.Pow10(3))  // 1000
fmt.Println(math.Pow10(-2)) // 0.01

Hypot — Panjang Hipotenusa #

math.Hypot(p, q) menghitung √(p² + q²) dengan akurasi numerik lebih baik dari implementasi manual:

// ANTI-PATTERN: hitung manual — rentan overflow untuk nilai besar
func jarakManual(x, y float64) float64 {
    return math.Sqrt(x*x + y*y) // bisa overflow jika x atau y sangat besar
}

// BENAR: gunakan Hypot — menangani edge case secara internal
fmt.Println(math.Hypot(3, 4))   // 5
fmt.Println(math.Hypot(5, 12))  // 13

// Jarak antara dua titik koordinat
func jarak(x1, y1, x2, y2 float64) float64 {
    return math.Hypot(x2-x1, y2-y1)
}

Logaritma dan Eksponen #

Log, Log2, Log10 #

// Log — logaritma natural (basis e)
fmt.Println(math.Log(math.E))   // 1
fmt.Println(math.Log(1))        // 0
fmt.Println(math.Log(0))        // -Inf
fmt.Println(math.Log(-1))       // NaN

// Log2 — logaritma basis 2
fmt.Println(math.Log2(1024))    // 10
fmt.Println(math.Log2(8))       // 3

// Log10 — logaritma basis 10
fmt.Println(math.Log10(1000))   // 3
fmt.Println(math.Log10(0.01))   // -2

// Logaritma basis N sembarang — rumus change of base
func logN(x, base float64) float64 {
    return math.Log(x) / math.Log(base)
}
fmt.Println(logN(81, 3)) // 4 (3^4 = 81)

Exp dan Exp2 #

// Exp — e^x (kebalikan dari Log)
fmt.Println(math.Exp(1))    // 2.718281828459045 (= e)
fmt.Println(math.Exp(0))    // 1
fmt.Println(math.Exp(3))    // 20.085536923187668

// Exp2 — 2^x (kebalikan dari Log2)
fmt.Println(math.Exp2(10))  // 1024
fmt.Println(math.Exp2(0.5)) // 1.4142135623730951 (= √2)

// Expm1 — e^x - 1, akurat untuk x mendekati nol
// Gunakan ini sebagai pengganti Exp(x)-1 untuk x yang sangat kecil
fmt.Println(math.Expm1(0.0001))         // 0.00010000500016667084
fmt.Println(math.Exp(0.0001) - 1)       // 0.00010000500016667084 (sama)
fmt.Println(math.Expm1(1e-20))          // 1e-20 (akurat)
fmt.Println(math.Exp(1e-20) - 1)        // 0 (kehilangan presisi!)

Trigonometri #

Semua fungsi trigonometri di math menggunakan radian, bukan derajat. Konversi dari derajat ke radian menggunakan rumus derajat × π / 180.

// Konversi derajat <-> radian
func keRadian(derajat float64) float64 {
    return derajat * math.Pi / 180
}

func keDerajat(radian float64) float64 {
    return radian * 180 / math.Pi
}

// Fungsi trigonometri dasar
fmt.Println(math.Sin(math.Pi / 2))    // 1 (sin 90°)
fmt.Println(math.Cos(0))              // 1 (cos 0°)
fmt.Println(math.Tan(math.Pi / 4))    // 0.9999999999999999 ≈ 1 (tan 45°)

// Sin dan Cos bersamaan (lebih efisien dari memanggil keduanya)
s, c := math.Sincos(math.Pi / 4)
fmt.Printf("sin(45°)=%.4f, cos(45°)=%.4f\n", s, c) // 0.7071, 0.7071

// Fungsi invers (arc)
fmt.Println(math.Asin(1))             // 1.5707963267948966 (= π/2)
fmt.Println(math.Acos(1))             // 0
fmt.Println(math.Atan(1))             // 0.7853981633974483 (= π/4)

// Atan2 — sudut dari koordinat (x, y), menangani semua kuadran
fmt.Println(math.Atan2(1, 1))         // π/4 (kuadran I)
fmt.Println(math.Atan2(1, -1))        // 3π/4 (kuadran II)
fmt.Println(math.Atan2(-1, -1))       // -3π/4 (kuadran III)

Pola: Konversi Koordinat #

// Konversi koordinat kartesian ke polar
func kePolar(x, y float64) (r, theta float64) {
    r = math.Hypot(x, y)
    theta = math.Atan2(y, x) // sudut dalam radian
    return
}

// Konversi koordinat polar ke kartesian
func keKartesian(r, theta float64) (x, y float64) {
    x = r * math.Cos(theta)
    y = r * math.Sin(theta)
    return
}

r, theta := kePolar(3, 4)
fmt.Printf("r=%.2f, theta=%.4f rad (%.2f°)\n",
    r, theta, keDerajat(theta)) // r=5.00, theta=0.9273 rad (53.13°)

Nilai Absolut, Min, dan Max #

Abs, Min, Max, dan Dim #

// Abs — nilai absolut
fmt.Println(math.Abs(-5.5))   // 5.5
fmt.Println(math.Abs(3.2))    // 3.2
fmt.Println(math.Abs(0))      // 0

// Min dan Max — nilai terkecil/terbesar dari dua float64
fmt.Println(math.Min(3.5, 7.2))   // 3.5
fmt.Println(math.Max(3.5, 7.2))   // 7.2
fmt.Println(math.Min(math.NaN(), 5)) // NaN — perhatikan perilaku ini

// Dim — max(x-y, 0) — berguna untuk perhitungan yang tidak boleh negatif
fmt.Println(math.Dim(5, 3))   // 2  (5-3=2)
fmt.Println(math.Dim(3, 5))   // 0  (3-5=-2, dikembalikan sebagai 0)
fmt.Println(math.Dim(5, 5))   // 0

// Pola: hitung sisa waktu (tidak boleh negatif)
func sisaWaktu(deadline, sekarang float64) float64 {
    return math.Dim(deadline, sekarang)
}
math.Min dan math.Max bekerja hanya dengan float64. Untuk integer (int, int64, dll), Go 1.21 menambahkan min() dan max() sebagai built-in function yang bisa digunakan langsung tanpa import apapun. Untuk versi Go sebelum 1.21, kamu perlu menulis perbandingan manual menggunakan if.

Signbit dan Copysign #

// Signbit — apakah nilai bertanda negatif (termasuk -0 dan -Inf)
fmt.Println(math.Signbit(-3.14))      // true
fmt.Println(math.Signbit(3.14))       // false
fmt.Println(math.Signbit(math.Inf(-1))) // true

// Copysign — salin tanda dari y ke x
fmt.Println(math.Copysign(5, -1))     // -5
fmt.Println(math.Copysign(5, 1))      // 5
fmt.Println(math.Copysign(-5, 1))     // 5

Package math/rand — Bilangan Acak #

Package math/rand menyediakan generator bilangan pseudo-random yang cepat, cocok untuk simulasi, testing, game, dan shuffle data. Ini bukan random yang kriptografis — untuk keamanan, gunakan crypto/rand.

Penggunaan Dasar (Go 1.20+) #

Sejak Go 1.20, math/rand secara otomatis menggunakan seed acak sehingga tidak perlu lagi memanggil rand.Seed() secara manual. Fungsi-fungsi top-level langsung bisa digunakan:

import (
    "fmt"
    "math/rand"
)

// Integer acak dalam [0, n)
fmt.Println(rand.Intn(100))     // angka acak 0-99
fmt.Println(rand.Intn(6) + 1)   // simulasi dadu: 1-6

// Float acak dalam [0.0, 1.0)
fmt.Println(rand.Float64())     // misal: 0.6046602879796196
fmt.Println(rand.Float32())

// Float dalam rentang [min, max)
func acakRentang(min, max float64) float64 {
    return min + rand.Float64()*(max-min)
}
fmt.Printf("%.2f\n", acakRentang(1.5, 3.5)) // misal: 2.37

rand.New dan rand.Source — Kontrol Penuh #

Untuk keperluan yang butuh reproducibility (misalnya testing atau simulasi yang harus bisa diulang), buat instance rand.Rand dengan seed yang sudah diketahui:

// Source dengan seed tetap — menghasilkan urutan yang sama setiap kali
src := rand.NewSource(42)
r := rand.New(src)

fmt.Println(r.Intn(100)) // selalu sama jika seed sama
fmt.Println(r.Intn(100))
fmt.Println(r.Intn(100))

// Instance ini tidak thread-safe — buat satu per goroutine
// atau gunakan sync.Mutex untuk akses concurrent

Shuffle — Acak Urutan Slice #

// Acak urutan slice
buah := []string{"apel", "jeruk", "mangga", "pisang", "durian"}
rand.Shuffle(len(buah), func(i, j int) {
    buah[i], buah[j] = buah[j], buah[i]
})
fmt.Println(buah) // urutan acak

// Ambil N elemen acak dari slice (sample tanpa pengembalian)
func ambilAcak(slice []string, n int) []string {
    salinan := make([]string, len(slice))
    copy(salinan, slice)
    rand.Shuffle(len(salinan), func(i, j int) {
        salinan[i], salinan[j] = salinan[j], salinan[i]
    })
    return salinan[:n]
}

sampel := ambilAcak(buah, 3)
fmt.Println(sampel) // 3 buah acak

Distribusi Statistik #

// NormFloat64 — distribusi normal standar (mean=0, stddev=1)
nilai := rand.NormFloat64()

// Konversi ke mean dan stddev tertentu: X = mean + stddev * NormFloat64()
mean := 170.0   // tinggi badan rata-rata (cm)
stddev := 7.0
tinggiBadan := mean + stddev*rand.NormFloat64()
fmt.Printf("Tinggi simulasi: %.1f cm\n", tinggiBadan)

// ExpFloat64 — distribusi eksponensial (rate=1)
// Berguna untuk simulasi waktu antar kejadian (queuing theory)
waktutunggu := rand.ExpFloat64()
fmt.Printf("Waktu tunggu simulasi: %.3f\n", waktutunggu)

Keamanan: math/rand vs crypto/rand #

flowchart TD
    A{Untuk apa\nbilangan acak ini?} --> B{Keamanan\nkriptografis?}
    B -- Ya --> C[crypto/rand]
    B -- Tidak --> D{Reproducible\nuntuk testing?}
    D -- Ya --> E[rand.New dengan\nseed tetap]
    D -- Tidak --> F[rand.Intn / rand.Float64\nlangsung - Go 1.20+]

    C --> G["Token, password,\nkunci enkripsi,\nCSRF token"]
    E --> H["Simulasi, unit test,\ndata dummy"]
    F --> I["Game, shuffle,\nsampling, UI"]
// math/rand — JANGAN gunakan untuk keamanan
token := fmt.Sprintf("%d", rand.Int63()) // ✗ bisa diprediksi

// crypto/rand — untuk token keamanan
import "crypto/rand"
import "encoding/hex"

b := make([]byte, 16)
crypto_rand.Read(b)
token := hex.EncodeToString(b) // ✓ kriptografis aman

Package math/big — Presisi Arbitrer #

Tipe numerik bawaan Go punya batas: int64 maksimal sekitar 9.2 × 10¹⁸ dan float64 kehilangan presisi untuk desimal tertentu. Package math/big menyediakan tiga tipe untuk melampaui batasan ini.

TipeKegunaan
big.IntInteger presisi arbitrer — faktorial besar, kriptografi
big.FloatFloating point presisi arbitrer — komputasi ilmiah, kalkulasi finansial
big.RatBilangan rasional eksak (p/q) — hindari error floating point sama sekali

big.Int — Integer Tanpa Batas #

import "math/big"

// Faktorial 100 — jauh melebihi int64
func faktorial(n int64) *big.Int {
    hasil := big.NewInt(1)
    for i := int64(2); i <= n; i++ {
        hasil.Mul(hasil, big.NewInt(i))
    }
    return hasil
}

fmt.Println(faktorial(20))  // 2432902008176640000
fmt.Println(faktorial(100)) // 93326215443944152681699238856266700490715968264381621468592963895217...

// Operasi big.Int
a := big.NewInt(1000000000000) // 1 triliun
b := big.NewInt(999999999999)

sum := new(big.Int).Add(a, b)
prod := new(big.Int).Mul(a, b)
fmt.Println(sum)  // 1999999999999
fmt.Println(prod) // 999999999999000000000000

// Parsing dari string
c, ok := new(big.Int).SetString("12345678901234567890", 10)
if ok {
    fmt.Println(c)
}

// Perbandingan
cmp := a.Cmp(b) // -1 (a < b), 0 (a == b), 1 (a > b)
fmt.Println(cmp) // 1 (a > b)

big.Float — Floating Point Presisi Tinggi #

// Hitung π dengan presisi 200 bit
func hitungPi() *big.Float {
    // Implementasi sederhana dengan Leibniz series (ilustrasi saja)
    prec := uint(200)
    pi := new(big.Float).SetPrec(prec)
    // ... implementasi aktual menggunakan algoritma yang lebih efisien
    return pi
}

// Penggunaan dasar big.Float
a := new(big.Float).SetPrec(256).SetFloat64(1.0)
b := new(big.Float).SetPrec(256).SetFloat64(3.0)

hasil := new(big.Float).Quo(a, b) // 1/3 dengan presisi tinggi
fmt.Println(hasil.Text('f', 50))  // 0.33333333333333333333333333333333333333333333333333

// Bandingkan dengan float64 biasa
fmt.Println(1.0 / 3.0) // 0.3333333333333333 (hanya 16 digit)

big.Rat — Bilangan Rasional Eksak #

big.Rat merepresentasikan bilangan sebagai pecahan p/q tanpa kehilangan presisi apapun. Ini adalah solusi terbaik untuk kalkulasi finansial atau sains yang butuh presisi eksak.

// big.Rat — tidak ada error floating point
a := big.NewRat(1, 10)  // 1/10 = 0.1
b := big.NewRat(2, 10)  // 2/10 = 0.2

sum := new(big.Rat).Add(a, b)
fmt.Println(sum)                    // 3/10
fmt.Println(sum.FloatString(1))     // 0.3 (eksak!)

// Bandingkan dengan float64
fmt.Println(0.1 + 0.2)             // 0.30000000000000004 (tidak eksak)
fmt.Println(0.1 + 0.2 == 0.3)      // false

// big.Rat untuk kalkulasi harga
harga := big.NewRat(9999, 100)     // Rp 99.99
pajak := big.NewRat(11, 100)       // 11%

nilaiPajak := new(big.Rat).Mul(harga, pajak)
total := new(big.Rat).Add(harga, nilaiPajak)
fmt.Println(total.FloatString(2))  // "110.99" — eksak

Perbandingan Float yang Aman #

Membandingkan float64 dengan == sering menghasilkan hasil yang tidak terduga karena representasi biner tidak selalu eksak. Gunakan epsilon comparison untuk membandingkan nilai float.

// ANTI-PATTERN: bandingkan float dengan == langsung
a := 0.1 + 0.2
b := 0.3
if a == b { // false! meski secara matematis sama
    fmt.Println("sama")
}

// BENAR: gunakan toleransi epsilon
const epsilon = 1e-9

func hampirSama(a, b float64) bool {
    return math.Abs(a-b) < epsilon
}

fmt.Println(hampirSama(0.1+0.2, 0.3)) // true

// Untuk perbandingan relatif (lebih robust untuk angka besar/kecil)
func hampirSamaRelatif(a, b, toleransi float64) bool {
    if a == b {
        return true
    }
    diff := math.Abs(a - b)
    norm := math.Max(math.Abs(a), math.Abs(b))
    return diff/norm < toleransi
}

fmt.Println(hampirSamaRelatif(1e10+0.001, 1e10, 1e-9)) // true
fmt.Println(hampirSamaRelatif(1.0, 2.0, 1e-9))          // false

Pola Penggunaan Nyata #

Menghitung Jarak Haversine (Koordinat Bumi) #

Jarak antara dua titik di permukaan bumi tidak bisa dihitung dengan Pythagoras biasa — perlu formula Haversine yang memperhitungkan kelengkungan bumi:

const radiusBumi = 6371.0 // km

func jarakHaversine(lat1, lon1, lat2, lon2 float64) float64 {
    // Konversi derajat ke radian
    dLat := keRadian(lat2 - lat1)
    dLon := keRadian(lon2 - lon1)
    lat1 = keRadian(lat1)
    lat2 = keRadian(lat2)

    a := math.Sin(dLat/2)*math.Sin(dLat/2) +
        math.Cos(lat1)*math.Cos(lat2)*
            math.Sin(dLon/2)*math.Sin(dLon/2)

    c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
    return radiusBumi * c
}

func keRadian(derajat float64) float64 {
    return derajat * math.Pi / 180
}

// Jakarta ke Surabaya
jarak := jarakHaversine(-6.2088, 106.8456, -7.2575, 112.7521)
fmt.Printf("Jarak Jakarta-Surabaya: %.0f km\n", jarak) // ~664 km

Statistik Deskriptif #

func statistik(data []float64) (mean, variance, stddev float64) {
    n := float64(len(data))
    if n == 0 {
        return
    }

    // Hitung mean
    for _, v := range data {
        mean += v
    }
    mean /= n

    // Hitung variance (Welford's method untuk stabilitas numerik)
    for _, v := range data {
        diff := v - mean
        variance += diff * diff
    }
    variance /= n

    stddev = math.Sqrt(variance)
    return
}

data := []float64{2, 4, 4, 4, 5, 5, 7, 9}
mean, variance, stddev := statistik(data)
fmt.Printf("Mean: %.2f\n", mean)        // 5.00
fmt.Printf("Variance: %.2f\n", variance) // 4.00
fmt.Printf("Stddev: %.2f\n", stddev)     // 2.00

Normalisasi Data (Min-Max Scaling) #

func normalisasi(data []float64) []float64 {
    if len(data) == 0 {
        return nil
    }

    minVal := data[0]
    maxVal := data[0]
    for _, v := range data[1:] {
        minVal = math.Min(minVal, v)
        maxVal = math.Max(maxVal, v)
    }

    rentang := maxVal - minVal
    if rentang == 0 {
        return make([]float64, len(data)) // semua nol jika semua sama
    }

    hasil := make([]float64, len(data))
    for i, v := range data {
        hasil[i] = (v - minVal) / rentang
    }
    return hasil
}

data := []float64{10, 20, 30, 40, 50}
fmt.Println(normalisasi(data)) // [0 0.25 0.5 0.75 1]

Generator ID Unik Sederhana #

import (
    "crypto/rand"
    "encoding/binary"
    "fmt"
    "math"
)

// ID numerik 6 digit acak (untuk kode OTP, PIN, dll)
func generateOTP() string {
    var b [8]byte
    crypto_rand.Read(b[:])
    n := binary.BigEndian.Uint64(b[:])
    otp := n % 1000000 // 6 digit
    return fmt.Sprintf("%06d", otp)
}

fmt.Println(generateOTP()) // "047291" (selalu 6 digit)

Kapan Beralih ke Alternatif #

Tetap gunakan math jika:
  ✓ Operasi matematika umum: akar, pangkat, trigonometri, logaritma
  ✓ Pembulatan dan nilai absolut untuk float64
  ✓ Bilangan acak untuk simulasi, game, testing, atau shuffle data
  ✓ Konstanta matematika (Pi, E, Phi, dll)
  ✓ Memeriksa NaN, Inf, dan nilai batas tipe numerik

Pertimbangkan math/big jika:
  ✗ Integer melebihi 9.2 × 10¹⁸ (batas int64)
  ✗ Kalkulasi finansial yang butuh presisi eksak (gunakan big.Rat)
  ✗ Kriptografi yang membutuhkan aritmetika modular presisi tinggi
  ✗ Komputasi ilmiah yang butuh lebih dari 15-16 digit presisi

Pertimbangkan crypto/rand jika:
  ✗ Membuat token keamanan, session ID, atau kunci enkripsi
  ✗ Semua kebutuhan bilangan acak yang berhubungan dengan keamanan

Pertimbangkan package eksternal jika:
  ✗ Aljabar linear, matriks, FFT → gonum.org/v1/gonum
  ✗ Statistik lanjutan → gonum.org/v1/gonum/stat
  ✗ Komputasi simbolik atau kalkulasi ilmiah kompleks → gonum ekosistem

Ringkasan #

  • Konstanta math.Pi, math.E, math.Phi — sudah tersedia dengan presisi float64 penuh; tidak perlu mendefinisikan ulang secara manual.
  • math.IsNaN dan math.IsInf — selalu gunakan fungsi ini untuk memeriksa NaN dan Inf; membandingkan NaN == NaN selalu false dan akan menjebak kamu.
  • math.Floor, math.Ceil, math.Round, math.Trunc — punya perilaku berbeda terutama untuk bilangan negatif; pahami perbedaannya sebelum digunakan.
  • Jangan gunakan float64 untuk uang — error representasi biner (0.1 + 0.2 ≠ 0.3) bisa menyebabkan selisih finansial; gunakan integer (sen) atau big.Rat.
  • math.Hypot(p, q) — lebih akurat dari math.Sqrt(p*p + q*q) karena menghindari overflow untuk nilai besar.
  • math/rand sudah auto-seeded sejak Go 1.20 — tidak perlu rand.Seed() lagi; gunakan rand.New(rand.NewSource(n)) hanya jika butuh reproducibility.
  • math/rand bukan untuk keamanan — gunakan crypto/rand untuk token, password, kunci enkripsi, dan semua kebutuhan kriptografis.
  • math/big menyediakan big.Int (integer tak terbatas), big.Float (presisi arbitrer), dan big.Rat (rasional eksak) — pilih sesuai kebutuhan presisi.
  • Bandingkan float dengan epsilon, bukan == — gunakan math.Abs(a-b) < epsilon untuk perbandingan yang robust terhadap error representasi.

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