Math #
Rust provides complete math operations right in the standard library — from basic arithmetic to trigonometric functions, logarithms, and rounding. All mathematical operations are available as methods on numeric types (i32, u64, f64, etc.) without the need to import additional modules. What differentiates Rust from other languages is its explicit handling of overflow: overflowing integer operations in debug mode will panic, and Rust provides four different overflow modes for complete control. This article discusses all the mathematical facilities in the standard library along with the rand crate for random numbers.
Mathematical Constants #
fn main() {
// Constant for 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...
// Numeric type limit
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);
}
Integer and Overflow Operations #
Rust provides complete control over integer overflow behavior — no “undefined behavior” like in C:
fn main() {
let a: i32 = i32::MAX; // 2147483647
// ANTI-PATTERN: overflow wraparound in release mode (panic in debug mode)
// let b = a + 1; // panic is debugged, wraps is released
// CORRECT: use methods that are explicit about overflow handling
// checked_* — returns None if overflow
println!("{:?}", a.checked_add(1)); // None
println!("{:?}", a.checked_add(-1)); // Some(2147483646)
println!("{:?}", 100i32.checked_mul(3)); // Some(300)
// saturating_* — stops at MIN or MAX limit
println!("{}", a.saturating_add(1)); // 2147483647 (stays at MAX)
println!("{}", i32::MIN.saturating_sub(1)); // -2147483648 (fixed at MIN)
println!("{}", 200u8.saturating_add(100)); // 255 (u8::MAX)
// wrapping_* — wraparound (arithmetic modular behavior)
println!("{}", a.wrapping_add(1)); // -2147483648 (wraparound to MIN)
println!("{}", 255u8.wrapping_add(1)); // 0
// overflowing_* — return (result, is_overflow)
let (hasil, overflow) = a.overflowing_add(1);
println!("hasil: {}, overflow: {}", hasil, overflow); // -2147483648, true
// Basic arithmetic
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 (sign follows numerator)
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
}
Advanced Integer Operations #
fn main() {
// GCD — Euclidean Algorithm
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
// Bit manipulation
let n: u8 = 0b1010_1100; // 172
println!("n = {:08b} = {}", n, n);
println!("n << 2 = {:08b}", n << 2); // left shift 2
println!("n >> 1 = {:08b}", n >> 1); // right shift 1
println!("n & 0x0F = {:08b}", n & 0x0F); // AND (take lower 4 bits)
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()); // number of bits 1
println!("count_zeros: {}", n.count_zeros()); // number of bits 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 to bytes and vice versa
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]
// From bytes back to integer
let kembali = u32::from_be_bytes(be);
println!("kembali: 0x{:08X}", kembali); // 0x12345678
}
Floating Point Operation #
fn main() {
let x: f64 = 2.0;
// Roots and powers
println!("√2 = {}", x.sqrt()); // 1.4142135...
println!("∛8 = {}", 8.0_f64.cbrt()); // 2.0
println!("2^10 = {}", x.powi(10)); // 1024.0 (integer rank)
println!("2^1.5 = {}", x.powf(1.5)); // 2.828...
println!("e^2 = {}", x.exp()); // 7,389...
println!("2^x = {}", x.exp2()); // 4.0
// Logarithm
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
// Absolute value, 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
// Rounding
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 (remove decimals)
println!("fract({}) = {}", f, f.fract()); // 0.7 (decimal only)
// Rounding to N decimals
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
// Check special value
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 is not equal to itself
let nan = f64::NAN;
println!("NaN == NaN: {}", nan == nan); // false!
println!("NaN.is_nan(): {}", nan.is_nan()); // true — the correct way
}
Trigonometry #
fn main() {
use std::f64::consts::PI;
// Trigonometric functions (arguments in radians)
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
// Convert degrees ↔ radians
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
// Inverse trigonometry (returns radians)
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 — angle of vector (y, x), range -π to π
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)));
// Hyperbolic
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 — calculate both at once (more efficient)
let (sin, cos) = (PI / 4.0).sin_cos();
println!("\nsin_cos(π/4) = ({:.4}, {:.4})", sin, cos); // (0.7071, 0.7071)
// Hypot — hypotenuse length: √(x² + y²)
println!("hypot(3, 4) = {}", 3.0_f64.hypot(4.0)); // 5.0
}
Numerical Comparison and Sorting #
fn main() {
// Integer: Ord — can sort directly
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 only (NaN not 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 and max of iterator
let data = vec![5, 2, 8, 1, 9];
println!("Min: {:?}", data.iter().min()); // Some(1)
println!("Max: {:?}", data.iter().max()); // Some(9)
// sum and product
let jumlah: i32 = data.iter().sum();
let produk: i32 = data.iter().product();
println!("Sum: {}, Product: {}", jumlah, produk);
// Avg
let rata: f64 = data.iter().map(|&x| x as f64).sum::<f64>() / data.len() as f64;
println!("Rata-rata: {:.2}", rata);
// Safe float comparison (avoid == for 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));
}
Random Number with rand
#
Standard library does not provide random numbers. Use the rand crate:
[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();
// Random integer in range (inclusive)
let n: i32 = rng.gen_range(1..=100);
println!("Angka acak 1-100: {}", n);
// Random float in [0.0, 1.0)
let f: f64 = rng.gen();
println!("Float acak: {:.4}", f);
// Float in range
let f_range: f64 = rng.gen_range(0.0..=10.0);
println!("Float 0-10: {:.4}", f_range);
// Random bool
let b: bool = rng.gen();
println!("Bool acak: {}", b);
// Uniform Distribution (more efficient for many samples from the same range)
let distribusi = Uniform::from(1..=6); // 6 sided dice
println!("Dadu: {:?}", (0..5).map(|_| distribusi.sample(&mut rng)).collect::<Vec<_>>());
// Select a random element from the slice
let buah = ["apel", "mangga", "jeruk", "durian", "rambutan"];
if let Some(pilihan) = buah.choose(&mut rng) {
println!("Buah acak: {}", pilihan);
}
// Select N random elements without repetition
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);
// Deterministic (reproducible) seed — useful for 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); // is always the same
}
Other Math Utilities #
fn main() {
// Absolute value — available for all signed numeric types
println!("{}", (-42_i32).abs()); // 42
println!("{}", (-3.14_f64).abs()); // 3.14
// signum — sign value (-1, 0, or 1)
println!("{}", (-5_i32).signum()); // -1
println!("{}", 0_i32.signum()); // 0
println!("{}", 5_i32.signum()); // 1
// Division and modulo with rounding down (floor div)
// Rust % gives the remainder with a numerator sign
// For floor division, use div_euclid and 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 (always positive)
println!("-7 div_euclid 3 = {}", (-7_i32).div_euclid(3)); // -3
// Check various numeric properties
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
// Convert between numeric types
let i: i32 = 42;
let f: f64 = i as f64; // casting — can be lossy for large values
let kembali: i32 = f as i32; // float to int — truncate, not round
// Safe conversion with 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)
}
Summary #
- Integer overflow handled explicitly — use
checked_*(returnNone),saturating_*(clamp at boundary),wrapping_*(modular), oroverflowing_*(return flag). Don’t leave overflow undefined.- Float cannot be
==ed directly —0.1 + 0.2 != 0.3due to IEEE 754 representation. Use comparison with epsilon:(a - b).abs() < f64::EPSILON.f64::NAN != f64::NAN— use.is_nan()instead of==to check NaN.- All math functions as methods —
x.sqrt(),x.ln(),x.sin(), not free functions. Consistent with Rust style.powivspowf—powi(n)for integer powers (faster),powf(x)for float powers.atan2(y, x)for vector angles — more robust thanatan(y/x)because it handles all quadrants and division by zero.sin_cos()is more efficient than two separate calls — the computer calculates both almost simultaneously internally.rem_euclidfor always positive modulo — different from Rust’s%where the sign follows the quantifier.- Use
Uniform::from(range)for multiple samples — more efficient thangen_rangein loops because the distribution is compiled once.- Deterministic seed for reproducibility —
StdRng::seed_from_u64(seed)produces the same sequence every time. Useful for testing and simulation.