Chapter 15 11 min read

Math

Numeric helpers for Glide. Floating-point functions bind directly to the C math library (libm); the integer helpers and float min/max/clamp are pure Glide so they monomorphize cleanly. The page also exports the PI and E constants.

Import

import stdlib::math::*;

Linking against libm is automatic — there is no flag to pass.

Surface at a glance

Group Items
Constants PI, E
Roots / powers / exp sqrt, pow, exp
Rounding floor, ceil, round, fabs
Trigonometry sin, cos, tan, atan2
Logarithms log, log2, log10
Integer helpers min_int, max_int, abs_int, sign_int, clamp_int, ipow, gcd, lcm
Float helpers min_f64, max_f64, clamp_f64

The 14 floating-point functions are thin extern fn bindings into libm; everything else is pure Glide. None of the math functions return !T/?T — there is nothing to unwrap. Float functions signal out-of-domain inputs with IEEE-754 NaN/inf (see IEEE semantics), and the integer helpers wrap or overflow silently on two's-complement targets rather than erroring.

Constants

Name Type Value
PI f64 3.141592653589793
E f64 2.718281828459045
pub const PI: f64 = 3.141592653589793;
pub const E:  f64 = 2.718281828459045;
import stdlib::math::*;

fn main() -> i32 {
    let area: f64 = PI * 2.0 * 2.0;   // PI * r^2 with r = 2
    let growth: f64 = E * E;          // e^2
    println!("area", area);
    println!("growth", growth);
    return 0;
}

Floating-point functions

These are extern fn bindings into libm. All take and return f64. They do not return !T/?T — out-of-domain inputs follow C/IEEE-754 semantics (e.g. sqrt of a negative returns NaN, log(0.0) returns -inf), so there is no error value to unwrap. See IEEE-754 NaN and inf for how those values behave downstream.

Roots, powers, exponentials

Function Signature Description
sqrt pub extern fn sqrt(x: f64) -> f64 Square root. Returns NaN for negative input.
pow pub extern fn pow(x: f64, y: f64) -> f64 x^y as a float. For integer exponents prefer ipow.
exp pub extern fn exp(x: f64) -> f64 e^x.
import stdlib::math::*;

fn main() -> i32 {
    let r: f64 = sqrt(9.0);       // 3.0
    let p: f64 = pow(2.0, 10.0);  // 1024.0
    let e2: f64 = exp(1.0);       // E (≈ 2.71828)
    println!("sqrt", r);
    println!("pow", p);
    println!("exp", e2);
    return 0;
}

Rounding

Function Signature Description
floor pub extern fn floor(x: f64) -> f64 Round toward -∞. floor(-2.3) == -3.0.
ceil pub extern fn ceil(x: f64) -> f64 Round toward +∞. ceil(-2.7) == -2.0.
round pub extern fn round(x: f64) -> f64 Round to nearest, ties away from zero. round(2.5) == 3.0.
fabs pub extern fn fabs(x: f64) -> f64 Absolute value (float). Use abs_int for ints.
import stdlib::math::*;

fn main() -> i32 {
    let f: f64 = floor(2.7);   //  2.0
    let c: f64 = ceil(2.1);    //  3.0
    let rn: f64 = round(2.5);  //  3.0
    let a: f64 = fabs(-1.5);   //  1.5
    println!("floor", f);
    println!("ceil", c);
    println!("round", rn);
    println!("fabs", a);
    return 0;
}

Trigonometry

Angles are in radians. Convert from degrees with deg * PI / 180.0.

Function Signature Description
sin pub extern fn sin(x: f64) -> f64 Sine. Argument in radians.
cos pub extern fn cos(x: f64) -> f64 Cosine. Argument in radians.
tan pub extern fn tan(x: f64) -> f64 Tangent. Argument in radians.
atan2 pub extern fn atan2(y: f64, x: f64) -> f64 Two-argument arctangent: full-circle angle of point (x, y) in radians.
import stdlib::math::*;

fn main() -> i32 {
    let s: f64 = sin(PI / 2.0);     // 1.0
    let co: f64 = cos(PI);          // -1.0
    let t: f64 = tan(PI / 4.0);     // 1.0
    let ang: f64 = atan2(1.0, 0.0); // PI / 2  (point straight up)
    println!("sin", s);
    println!("cos", co);
    println!("tan", t);
    println!("atan2", ang);
    return 0;
}

Logarithms

Function Signature Description
log pub extern fn log(x: f64) -> f64 Natural log (base e). Domain x > 0.
log2 pub extern fn log2(x: f64) -> f64 Log base 2.
log10 pub extern fn log10(x: f64) -> f64 Log base 10.
import stdlib::math::*;

fn main() -> i32 {
    let l: f64 = log(E);          // 1.0
    let l2: f64 = log2(8.0);      // 3.0
    let l10: f64 = log10(1000.0); // 3.0
    println!("log", l);
    println!("log2", l2);
    println!("log10", l10);
    return 0;
}

IEEE-754 NaN and inf

Because the float functions bind straight to libm, out-of-domain inputs produce IEEE-754 special values instead of raising an error. The important consequences:

  • sqrt(-1.0), log(-1.0), pow(-1.0, 0.5)NaN.
  • log(0.0)-inf; pow(0.0, -1.0)+inf.
  • A NaN is unordered: every comparison against it (==, <, >) is false, including nan == nan. Detect one with the self-inequality trick x != x.
  • NaN propagates through arithmetic and through min_f64/max_f64/clamp_f64 (they are plain </> comparisons, so a NaN operand can pass through unchanged).
import stdlib::math::*;

fn main() -> i32 {
    let nan: f64 = sqrt(-1.0);       // NaN
    let neginf: f64 = log(0.0);      // -inf
    let is_nan: bool = nan != nan;   // true  — the canonical NaN test
    println!("nan", nan);
    println!("neginf", neginf);
    println!("is_nan", is_nan);
    println!("min-with-nan", min_f64(nan, 1.0));  // NaN propagates
    return 0;
}

Integer helpers

Pure-Glide functions over i32. They never allocate and never fail; results are plain i32 (no !T).

Function Signature Description
min_int pub fn min_int(a: i32, b: i32) -> i32 Smaller of two ints.
max_int pub fn max_int(a: i32, b: i32) -> i32 Larger of two ints.
abs_int pub fn abs_int(a: i32) -> i32 Absolute value. abs_int(INT_MIN) overflows.
sign_int pub fn sign_int(a: i32) -> i32 Sign: -1, 0, or +1.
clamp_int pub fn clamp_int(x: i32, lo: i32, hi: i32) -> i32 Clamp into inclusive [lo, hi].
ipow pub fn ipow(a: i32, b: i32) -> i32 a^b for non-negative b, by repeated squaring (O(log b)). Wraps on overflow.
gcd pub fn gcd(a: i32, b: i32) -> i32 Greatest common divisor (Euclidean). Always non-negative.
lcm pub fn lcm(a: i32, b: i32) -> i32 Least common multiple. Returns 0 if either argument is 0.
import stdlib::math::*;

fn main() -> i32 {
    let mn: i32 = min_int(7, 3);        // 3
    let mx: i32 = max_int(7, 3);        // 7
    let av: i32 = abs_int(-5);          // 5
    let sg: i32 = sign_int(-9);         // -1
    let cl: i32 = clamp_int(15, 0, 10); // 10
    let ip: i32 = ipow(2, 10);          // 1024
    let g: i32 = gcd(12, 18);           // 6
    let l: i32 = lcm(4, 6);             // 12
    println!("min_int", mn);
    println!("max_int", mx);
    println!("abs_int", av);
    println!("sign_int", sg);
    println!("clamp_int", cl);
    println!("ipow", ip);
    println!("gcd", g);
    println!("lcm", l);
    return 0;
}

gcd operates on absolute values, so gcd(-12, 8) == 4 and gcd(0, 0) == 0. lcm is computed as abs_int(a / gcd(a, b) * b) to limit overflow on the intermediate product.

Float helpers

Pure-Glide min/max/clamp over f64. NaN behaviour follows libc semantics — a NaN operand propagates.

Function Signature Description
min_f64 pub fn min_f64(a: f64, b: f64) -> f64 Smaller of two f64s.
max_f64 pub fn max_f64(a: f64, b: f64) -> f64 Larger of two f64s.
clamp_f64 pub fn clamp_f64(x: f64, lo: f64, hi: f64) -> f64 Clamp into [lo, hi]. See clamp_int caveats.
import stdlib::math::*;

fn main() -> i32 {
    let mn: f64 = min_f64(1.5, 2.5);        // 1.5
    let mx: f64 = max_f64(1.5, 2.5);        // 2.5
    let cl: f64 = clamp_f64(2.0, 0.0, 1.0); // 1.0
    println!("min_f64", mn);
    println!("max_f64", mx);
    println!("clamp_f64", cl);
    return 0;
}

Worked examples

Euclidean distance

sqrt plus a sum of squares gives the straight-line distance between two points — the building block for collision, nearest-neighbour, and physics code.

import stdlib::math::*;

fn distance(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
    let dx: f64 = x2 - x1;
    let dy: f64 = y2 - y1;
    return sqrt(dx * dx + dy * dy);
}

fn main() -> i32 {
    let d: f64 = distance(0.0, 0.0, 3.0, 4.0);  // 5.0
    println!("distance", d);
    return 0;
}

Degrees and radians

Trig functions take radians. Wrap the conversion factor PI / 180.0 once so the call sites read cleanly.

import stdlib::math::*;

fn to_radians(deg: f64) -> f64 { return deg * PI / 180.0; }
fn to_degrees(rad: f64) -> f64 { return rad * 180.0 / PI; }

fn main() -> i32 {
    let r: f64 = to_radians(90.0);   // PI / 2
    println!("sin90", sin(r));       // 1.0
    println!("degrees", to_degrees(r)); // 90.0
    return 0;
}

Reducing a fraction with gcd / lcm

Dividing numerator and denominator by their gcd reduces a fraction to lowest terms. lcm finds a common denominator. Both operate on absolute values and never allocate.

import stdlib::math::*;

fn main() -> i32 {
    let num: i32 = 84;
    let den: i32 = 126;
    let g: i32 = gcd(num, den);   // 42
    println!("reduced", num / g, den / g);   // 2 3

    let l: i32 = lcm(lcm(4, 6), 8);          // 24
    println!("lcm", l);

    // gcd uses magnitudes; the zero edge cases:
    println!("gcd-neg", gcd(-12, 8));   // 4
    println!("gcd-zero", gcd(0, 0));    // 0
    println!("lcm-zero", lcm(0, 5));    // 0
    return 0;
}

Clamping inputs, splitting sign and magnitude

clamp_int/clamp_f64 keep untrusted values inside a range; sign_int + abs_int split a signed value into direction and magnitude.

import stdlib::math::*;

fn main() -> i32 {
    let vol: i32 = clamp_int(137, 0, 100);   // 100 — slider saturates
    let alpha: f64 = clamp_f64(1.4, 0.0, 1.0); // 1.0

    let v: i32 = -42;
    println!("dir", sign_int(v));   // -1
    println!("mag", abs_int(v));    // 42
    println!("vol", vol);
    println!("alpha", alpha);

    // clamp_int(x, lo, hi) is exactly max_int(lo, min_int(hi, x)):
    println!("manual", max_int(0, min_int(100, 137)));  // 100
    return 0;
}

Exact integer powers — ipow vs pow

pow works in floating point and can drift off an exact integer; ipow stays in i32 (wrapping on overflow). Use ipow whenever both operands are integers and the result fits in i32.

import stdlib::math::*;

fn main() -> i32 {
    let exact: i32 = ipow(10, 9);     // 1000000000, exact
    let approx: f64 = pow(10.0, 9.0); // may be 999999999.9999999...
    let recovered: i32 = floor(approx + 0.5) as i32;
    println!("ipow", exact);
    println!("pow-recovered", recovered);
    return 0;
}

See also

  • `stdlib::random` — random number generation, often clamped with clamp_int/clamp_f64.
  • The book's numeric-types chapter for i32/f64 literals, casts (as), and integer overflow semantics.