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_*(returnNone),saturating_*(clamp di batas),wrapping_*(modular), atauoverflowing_*(return flag). Jangan biarkan overflow undefined.- Float tidak bisa di-
==secara langsung —0.1 + 0.2 != 0.3akibat 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 method —
x.sqrt(),x.ln(),x.sin(), bukan fungsi bebas. Konsisten dengan gaya Rust.powivspowf—powi(n)untuk pangkat integer (lebih cepat),powf(x)untuk pangkat float.atan2(y, x)untuk sudut vektor — lebih robust dariatan(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_eucliduntuk modulo yang selalu positif — bergeda dari%Rust yang tanda-nya mengikuti pembilang.- Gunakan
Uniform::from(range)untuk banyak sampel — lebih efisien darigen_rangedalam loop karena distribusi dikompilasi sekali.- Seed deterministik untuk reprodusibilitas —
StdRng::seed_from_u64(seed)menghasilkan urutan yang sama setiap kali. Berguna untuk testing dan simulasi.
← Sebelumnya: IO