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.
| Fungsi | Perilaku | Contoh (+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 menggunakanfloat64. Representasi binerfloat64tidak bisa merepresentasikan semua desimal dengan tepat — misalnya0.1 + 0.2menghasilkan0.30000000000000004, bukan0.3. Untuk kalkulasi finansial, gunakan integer (sen/sen terkecil) atau packagemath/bigdenganbig.Rat/big.Floatyang 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.Mindanmath.Maxbekerja hanya denganfloat64. Untuk integer (int,int64, dll), Go 1.21 menambahkanmin()danmax()sebagai built-in function yang bisa digunakan langsung tanpa import apapun. Untuk versi Go sebelum 1.21, kamu perlu menulis perbandingan manual menggunakanif.
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.
| Tipe | Kegunaan |
|---|---|
big.Int | Integer presisi arbitrer — faktorial besar, kriptografi |
big.Float | Floating point presisi arbitrer — komputasi ilmiah, kalkulasi finansial |
big.Rat | Bilangan 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 presisifloat64penuh; tidak perlu mendefinisikan ulang secara manual.math.IsNaNdanmath.IsInf— selalu gunakan fungsi ini untuk memeriksa NaN dan Inf; membandingkanNaN == NaNselalufalsedan akan menjebak kamu.math.Floor,math.Ceil,math.Round,math.Trunc— punya perilaku berbeda terutama untuk bilangan negatif; pahami perbedaannya sebelum digunakan.- Jangan gunakan
float64untuk uang — error representasi biner (0.1 + 0.2 ≠ 0.3) bisa menyebabkan selisih finansial; gunakan integer (sen) ataubig.Rat.math.Hypot(p, q)— lebih akurat darimath.Sqrt(p*p + q*q)karena menghindari overflow untuk nilai besar.math/randsudah auto-seeded sejak Go 1.20 — tidak perlurand.Seed()lagi; gunakanrand.New(rand.NewSource(n))hanya jika butuh reproducibility.math/randbukan untuk keamanan — gunakancrypto/randuntuk token, password, kunci enkripsi, dan semua kebutuhan kriptografis.math/bigmenyediakanbig.Int(integer tak terbatas),big.Float(presisi arbitrer), danbig.Rat(rasional eksak) — pilih sesuai kebutuhan presisi.- Bandingkan float dengan epsilon, bukan
==— gunakanmath.Abs(a-b) < epsilonuntuk perbandingan yang robust terhadap error representasi.
← Sebelumnya: IO