Math

Math #

Rust menyediakan operasi matematika yang lengkap langsung di standard library — mulai dari aritmetika dasar hingga fungsi trigonometri, logaritma, dan pembulatan. Semua operasi matematika tersedia sebagai method pada tipe numerik (i32, u64, f64, dll.) tanpa perlu import modul tambahan. Yang membedakan Rust dari bahasa lain adalah penanganan overflow secara eksplisit: operasi integer yang overflow di debug mode akan panic, dan Rust menyediakan empat mode overflow yang berbeda untuk kontrol penuh. Artikel ini membahas semua fasilitas matematika di standard library beserta crate rand untuk angka acak.

Konstanta Matematika #

fn main() {
    // Konstanta untuk f64
    println!("π  = {:.15}", std::f64::consts::PI);         // 3.141592653589793
    println!("e  = {:.15}", std::f64::consts::E);           // 2.718281828459045
    println!("√2 = {:.15}", std::f64::consts::SQRT_2);      // 1.4142135623730951
    println!("ln2= {:.15}", std::f64::consts::LN_2);        // 0.6931471805599453
    println!("log2(e) = {}", std::f64::consts::LOG2_E);
    println!("log10(e) = {}", std::f64::consts::LOG10_E);
    println!("τ (2π) = {:.6}", std::f64::consts::TAU);      // 6.283185...

    // Batas tipe numerik
    println!("\n--- Batas Integer ---");
    println!("i8:  {} .. {}", i8::MIN, i8::MAX);            // -128 .. 127
    println!("i32: {} .. {}", i32::MIN, i32::MAX);          // -2147483648 .. 2147483647
    println!("i64: {} .. {}", i64::MIN, i64::MAX);
    println!("u8:  {} .. {}", u8::MIN, u8::MAX);            // 0 .. 255
    println!("u32: {} .. {}", u32::MIN, u32::MAX);          // 0 .. 4294967295

    println!("\n--- Batas Float ---");
    println!("f32 max: {:.2e}", f32::MAX);                   // 3.40e38
    println!("f64 max: {:.2e}", f64::MAX);                   // 1.80e308
    println!("f64 min positif: {:.2e}", f64::MIN_POSITIVE);  // 2.23e-308
    println!("f64 epsilon: {:.2e}", f64::EPSILON);           // 2.22e-16
    println!("NaN: {}", f64::NAN);
    println!("Infinity: {}", f64::INFINITY);
    println!("Neg Infinity: {}", f64::NEG_INFINITY);
}

Operasi Integer dan Overflow #

Rust memberikan kontrol penuh atas perilaku overflow integer — tidak ada “undefined behavior” seperti di C:

fn main() {
    let a: i32 = i32::MAX;  // 2147483647

    // ANTI-PATTERN: overflow wraparound di release mode (panic di debug mode)
    // let b = a + 1;  // panic di debug, wraps di release

    // BENAR: gunakan metode yang eksplisit tentang penanganan overflow

    // checked_* — mengembalikan None jika overflow
    println!("{:?}", a.checked_add(1));      // None
    println!("{:?}", a.checked_add(-1));     // Some(2147483646)
    println!("{:?}", 100i32.checked_mul(3)); // Some(300)

    // saturating_* — berhenti di batas MIN atau MAX
    println!("{}", a.saturating_add(1));     // 2147483647 (tetap di MAX)
    println!("{}", i32::MIN.saturating_sub(1)); // -2147483648 (tetap di MIN)
    println!("{}", 200u8.saturating_add(100));  // 255 (u8::MAX)

    // wrapping_* — wraparound (perilaku modular arithmetic)
    println!("{}", a.wrapping_add(1));       // -2147483648 (wraparound ke MIN)
    println!("{}", 255u8.wrapping_add(1));   // 0

    // overflowing_* — kembalikan (hasil, apakah_overflow)
    let (hasil, overflow) = a.overflowing_add(1);
    println!("hasil: {}, overflow: {}", hasil, overflow);  // -2147483648, true

    // Aritmetika dasar
    println!("\n--- Aritmetika Dasar ---");
    println!("7 / 2 = {}", 7 / 2);          // 3 (integer division, truncate)
    println!("7 % 2 = {}", 7 % 2);          // 1 (remainder)
    println!("-7 % 2 = {}", -7 % 2);        // -1 (tanda mengikuti pembilang)
    println!("7.0 / 2.0 = {}", 7.0_f64 / 2.0); // 3.5
    println!("2_i32.pow(10) = {}", 2_i32.pow(10)); // 1024
    println!("2_u64.pow(32) = {}", 2_u64.pow(32)); // 4294967296
}

Operasi Integer Lanjutan #

fn main() {
    // GCD — Algoritma Euclidean
    fn gcd(a: u64, b: u64) -> u64 {
        if b == 0 { a } else { gcd(b, a % b) }
    }

    // LCM
    fn lcm(a: u64, b: u64) -> u64 {
        a / gcd(a, b) * b
    }

    println!("GCD(48, 18) = {}", gcd(48, 18));   // 6
    println!("LCM(4, 6) = {}", lcm(4, 6));        // 12

    // Manipulasi bit
    let n: u8 = 0b1010_1100;  // 172
    println!("n = {:08b} = {}", n, n);
    println!("n << 2 = {:08b}", n << 2);          // shift kiri 2
    println!("n >> 1 = {:08b}", n >> 1);           // shift kanan 1
    println!("n & 0x0F = {:08b}", n & 0x0F);       // AND (ambil 4 bit bawah)
    println!("n | 0x0F = {:08b}", n | 0x0F);       // OR
    println!("n ^ 0xFF = {:08b}", n ^ 0xFF);       // XOR
    println!("!n = {:08b}", !n);                    // NOT
    println!("count_ones: {}", n.count_ones());     // jumlah bit 1
    println!("count_zeros: {}", n.count_zeros());   // jumlah bit 0
    println!("leading_zeros: {}", n.leading_zeros());
    println!("trailing_zeros: {}", n.trailing_zeros());
    println!("reverse_bits: {:08b}", n.reverse_bits());
    println!("rotate_left(3): {:08b}", n.rotate_left(3));

    // Integer ke bytes dan sebaliknya
    let angka: u32 = 0x12345678;
    println!("\nu32 = 0x{:08X}", angka);
    let be = angka.to_be_bytes();   // big-endian
    let le = angka.to_le_bytes();   // little-endian
    println!("big-endian bytes: {:02X?}", be);    // [12, 34, 56, 78]
    println!("little-endian bytes: {:02X?}", le); // [78, 56, 34, 12]

    // Dari bytes kembali ke integer
    let kembali = u32::from_be_bytes(be);
    println!("kembali: 0x{:08X}", kembali);  // 0x12345678
}

Operasi Floating Point #

fn main() {
    let x: f64 = 2.0;

    // Akar dan pangkat
    println!("√2 = {}", x.sqrt());            // 1.4142135...
    println!("∛8 = {}", 8.0_f64.cbrt());      // 2.0
    println!("2^10 = {}", x.powi(10));         // 1024.0 (pangkat integer)
    println!("2^1.5 = {}", x.powf(1.5));       // 2.828...
    println!("e^2 = {}", x.exp());             // 7.389...
    println!("2^x = {}", x.exp2());            // 4.0

    // Logaritma
    println!("\n--- Logaritma ---");
    println!("ln(e) = {}", std::f64::consts::E.ln());     // 1.0
    println!("ln(2) = {}", 2.0_f64.ln());                 // 0.693...
    println!("log2(8) = {}", 8.0_f64.log2());             // 3.0
    println!("log10(1000) = {}", 1000.0_f64.log10());     // 3.0
    println!("log_5(125) = {}", 125.0_f64.log(5.0));      // 3.0

    // Nilai absolut, min, max
    println!("\n--- Absolut, Min, Max ---");
    println!("|−5.5| = {}", (-5.5_f64).abs());            // 5.5
    println!("|-5| = {}", (-5_i32).abs());                 // 5
    println!("min(3, 7) = {}", 3.0_f64.min(7.0));         // 3.0
    println!("max(3, 7) = {}", 3.0_f64.max(7.0));         // 7.0
    println!("clamp(15, 0, 10) = {}", 15.0_f64.clamp(0.0, 10.0)); // 10.0
    println!("clamp(-5, 0, 10) = {}", (-5.0_f64).clamp(0.0, 10.0)); // 0.0

    // Pembulatan
    println!("\n--- Pembulatan ---");
    let f = 3.7_f64;
    println!("floor({}) = {}", f, f.floor());   // 3.0
    println!("ceil({}) = {}", f, f.ceil());      // 4.0
    println!("round({}) = {}", f, f.round());    // 4.0
    println!("trunc({}) = {}", f, f.trunc());    // 3.0 (hapus desimal)
    println!("fract({}) = {}", f, f.fract());    // 0.7 (hanya desimal)

    // Pembulatan ke N desimal
    fn bulat_n_desimal(x: f64, n: u32) -> f64 {
        let faktor = 10f64.powi(n as i32);
        (x * faktor).round() / faktor
    }
    println!("π rounded 4dp: {}", bulat_n_desimal(std::f64::consts::PI, 4)); // 3.1416

    // Cek nilai khusus
    println!("\n--- Nilai Khusus ---");
    println!("NaN is_nan: {}", f64::NAN.is_nan());         // true
    println!("Inf is_infinite: {}", f64::INFINITY.is_infinite()); // true
    println!("3.14 is_finite: {}", 3.14_f64.is_finite());  // true
    println!("0.0 is_sign_positive: {}", 0.0_f64.is_sign_positive()); // true
    println!("-0.0 is_sign_negative: {}", (-0.0_f64).is_sign_negative()); // true

    // NaN tidak sama dengan dirinya sendiri
    let nan = f64::NAN;
    println!("NaN == NaN: {}", nan == nan);  // false!
    println!("NaN.is_nan(): {}", nan.is_nan());  // true — cara yang benar
}

Trigonometri #

fn main() {
    use std::f64::consts::PI;

    // Fungsi trigonometri (argumen dalam radian)
    println!("--- Trigonometri (radian) ---");
    println!("sin(π/6) = {:.4}", (PI / 6.0).sin());   // 0.5
    println!("cos(π/3) = {:.4}", (PI / 3.0).cos());   // 0.5
    println!("tan(π/4) = {:.4}", (PI / 4.0).tan());   // 1.0

    // Konversi derajat ↔ radian
    fn ke_radian(derajat: f64) -> f64 { derajat * PI / 180.0 }
    fn ke_derajat(radian: f64) -> f64 { radian * 180.0 / PI }

    println!("\n--- Trigonometri (derajat) ---");
    println!("sin(30°) = {:.4}", ke_radian(30.0).sin());   // 0.5000
    println!("cos(60°) = {:.4}", ke_radian(60.0).cos());   // 0.5000
    println!("tan(45°) = {:.4}", ke_radian(45.0).tan());   // 1.0000

    // Invers trigonometri (mengembalikan radian)
    println!("\n--- Invers Trigonometri ---");
    println!("asin(0.5) = {:.4}° = 30°", ke_derajat(0.5_f64.asin()));
    println!("acos(0.5) = {:.4}° = 60°", ke_derajat(0.5_f64.acos()));
    println!("atan(1.0) = {:.4}° = 45°", ke_derajat(1.0_f64.atan()));

    // atan2 — sudut dari vektor (y, x), range -π hingga π
    println!("atan2(1, 1) = {:.4}° = 45°", ke_derajat(1.0_f64.atan2(1.0)));
    println!("atan2(1, -1) = {:.4}° = 135°", ke_derajat(1.0_f64.atan2(-1.0)));

    // Hiperbolik
    println!("\n--- Hiperbolik ---");
    println!("sinh(1) = {:.4}", 1.0_f64.sinh());
    println!("cosh(1) = {:.4}", 1.0_f64.cosh());
    println!("tanh(1) = {:.4}", 1.0_f64.tanh());

    // sin_cos — hitung keduanya sekaligus (lebih efisien)
    let (sin, cos) = (PI / 4.0).sin_cos();
    println!("\nsin_cos(π/4) = ({:.4}, {:.4})", sin, cos);  // (0.7071, 0.7071)

    // Hypot — panjang hipotenusa: √(x² + y²)
    println!("hypot(3, 4) = {}", 3.0_f64.hypot(4.0));  // 5.0
}

Perbandingan dan Sorting Numerik #

fn main() {
    // Integer: Ord — bisa sort langsung
    let mut angka = vec![5, 2, 8, 1, 9, 3];
    angka.sort();
    println!("Sorted: {:?}", angka);

    angka.sort_by(|a, b| b.cmp(a));  // descending
    println!("Descending: {:?}", angka);

    // Float: PartialOrd saja (NaN tidak comparable)
    let mut floats = vec![3.14, 1.0, 2.718, 0.5];
    floats.sort_by(|a, b| a.partial_cmp(b).unwrap());
    println!("Floats sorted: {:?}", floats);

    // sort_by_key
    let mut pasangan = vec![(3, "tiga"), (1, "satu"), (2, "dua")];
    pasangan.sort_by_key(|&(k, _)| k);
    println!("By key: {:?}", pasangan);

    // min dan max dari iterator
    let data = vec![5, 2, 8, 1, 9];
    println!("Min: {:?}", data.iter().min());  // Some(1)
    println!("Max: {:?}", data.iter().max());  // Some(9)

    // sum dan product
    let jumlah: i32 = data.iter().sum();
    let produk: i32 = data.iter().product();
    println!("Sum: {}, Product: {}", jumlah, produk);

    // Rata-rata
    let rata: f64 = data.iter().map(|&x| x as f64).sum::<f64>() / data.len() as f64;
    println!("Rata-rata: {:.2}", rata);

    // Perbandingan float yang aman (hindari == untuk float)
    fn kira_sama(a: f64, b: f64, epsilon: f64) -> bool {
        (a - b).abs() < epsilon
    }
    println!("0.1 + 0.2 ≈ 0.3: {}", kira_sama(0.1 + 0.2, 0.3, f64::EPSILON * 10.0));
}

Angka Acak dengan rand #

Standard library tidak menyediakan angka acak. Gunakan crate rand:

[dependencies]
rand = "0.8"
use rand::{Rng, thread_rng};
use rand::seq::SliceRandom;
use rand::distributions::{Distribution, Uniform};

fn main() {
    let mut rng = thread_rng();

    // Integer acak dalam range (inklusif)
    let n: i32 = rng.gen_range(1..=100);
    println!("Angka acak 1-100: {}", n);

    // Float acak dalam [0.0, 1.0)
    let f: f64 = rng.gen();
    println!("Float acak: {:.4}", f);

    // Float dalam range
    let f_range: f64 = rng.gen_range(0.0..=10.0);
    println!("Float 0-10: {:.4}", f_range);

    // Bool acak
    let b: bool = rng.gen();
    println!("Bool acak: {}", b);

    // Distribusi Uniform (lebih efisien untuk banyak sampel dari range yang sama)
    let distribusi = Uniform::from(1..=6);  // dadu 6 sisi
    println!("Dadu: {:?}", (0..5).map(|_| distribusi.sample(&mut rng)).collect::<Vec<_>>());

    // Pilih elemen acak dari slice
    let buah = ["apel", "mangga", "jeruk", "durian", "rambutan"];
    if let Some(pilihan) = buah.choose(&mut rng) {
        println!("Buah acak: {}", pilihan);
    }

    // Pilih N elemen acak tanpa pengulangan
    let pilihan_n: Vec<&&str> = buah.choose_multiple(&mut rng, 3).collect();
    println!("3 buah acak: {:?}", pilihan_n);

    // Shuffle slice
    let mut kartu: Vec<i32> = (1..=10).collect();
    kartu.shuffle(&mut rng);
    println!("Kartu diacak: {:?}", kartu);

    // Seed deterministik (reproducible) — berguna untuk testing
    use rand::SeedableRng;
    use rand::rngs::StdRng;
    let seed: u64 = 42;
    let mut rng_seed = StdRng::seed_from_u64(seed);
    let acak_seed: i32 = rng_seed.gen_range(1..=100);
    println!("Dengan seed {}: {}", seed, acak_seed);  // selalu sama
}

Utilitas Matematika Lainnya #

fn main() {
    // Nilai absolut — tersedia untuk semua tipe numerik signed
    println!("{}", (-42_i32).abs());     // 42
    println!("{}", (-3.14_f64).abs());   // 3.14

    // signum — tanda nilai (-1, 0, atau 1)
    println!("{}", (-5_i32).signum());   // -1
    println!("{}", 0_i32.signum());      // 0
    println!("{}", 5_i32.signum());      // 1

    // Divisi dan modulo dengan pembulatan ke bawah (floor div)
    // Rust % memberikan sisa dengan tanda pembilang
    // Untuk floor division, gunakan div_euclid dan rem_euclid
    println!("\n--- Euclidean Division ---");
    println!("-7 % 3 = {}", -7_i32 % 3);              // -1 (Rust)
    println!("-7 rem_euclid 3 = {}", (-7_i32).rem_euclid(3)); // 2 (selalu positif)
    println!("-7 div_euclid 3 = {}", (-7_i32).div_euclid(3)); // -3

    // Cek berbagai properti numerik
    println!("\n--- Properti Numerik ---");
    println!("16 is_power_of_two: {}", 16_u32.is_power_of_two());  // true
    println!("12 is_power_of_two: {}", 12_u32.is_power_of_two());  // false
    println!("next_power_of_two(5): {}", 5_u32.next_power_of_two());  // 8
    println!("leading_zeros(1): {}", 1_u32.leading_zeros());  // 31

    // Konversi antara tipe numerik
    let i: i32 = 42;
    let f: f64 = i as f64;        // casting — bisa lossy untuk nilai besar
    let kembali: i32 = f as i32;  // float ke int — truncate, tidak round

    // Konversi yang aman dengan TryFrom/TryInto
    use std::convert::TryInto;
    let besar: i64 = 300;
    let kecil: Result<i8, _> = besar.try_into();
    println!("\n300 as i8: {:?}", kecil);  // Err (300 > i8::MAX)

    let kecil2: i64 = 100;
    let ok: Result<i8, _> = kecil2.try_into();
    println!("100 as i8: {:?}", ok);  // Ok(100)
}

Ringkasan #

  • Overflow integer ditangani eksplisit — gunakan checked_* (return None), saturating_* (clamp di batas), wrapping_* (modular), atau overflowing_* (return flag). Jangan biarkan overflow undefined.
  • Float tidak bisa di-== secara langsung0.1 + 0.2 != 0.3 akibat representasi IEEE 754. Gunakan perbandingan dengan epsilon: (a - b).abs() < f64::EPSILON.
  • f64::NAN != f64::NAN — gunakan .is_nan() bukan == untuk cek NaN.
  • Semua fungsi matematika sebagai methodx.sqrt(), x.ln(), x.sin(), bukan fungsi bebas. Konsisten dengan gaya Rust.
  • powi vs powfpowi(n) untuk pangkat integer (lebih cepat), powf(x) untuk pangkat float.
  • atan2(y, x) untuk sudut vektor — lebih robust dari atan(y/x) karena menangani semua kuadran dan pembagian dengan nol.
  • sin_cos() lebih efisien dari dua pemanggilan terpisah — komputer menghitung keduanya hampir bersamaan secara internal.
  • rem_euclid untuk modulo yang selalu positif — bergeda dari % Rust yang tanda-nya mengikuti pembilang.
  • Gunakan Uniform::from(range) untuk banyak sampel — lebih efisien dari gen_range dalam loop karena distribusi dikompilasi sekali.
  • Seed deterministik untuk reprodusibilitasStdRng::seed_from_u64(seed) menghasilkan urutan yang sama setiap kali. Berguna untuk testing dan simulasi.

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