zig-skills/references/std-math.md

12 KiB

std.math

Mathematical functions, constants, and utilities. Provides floating-point operations, trigonometry, integer arithmetic with overflow checking, and arbitrary-precision integers.

Quick Reference

Category Functions
Constants e, pi, phi, tau, sqrt2, ln2, ln10
Trig sin, cos, tan, asin, acos, atan, atan2
Hyperbolic sinh, cosh, tanh, asinh, acosh, atanh
Exponential exp, exp2, expm1, log, log2, log10, log1p
Powers/Roots pow, powi, sqrt, cbrt, hypot
Rounding floor, ceil, round, trunc
Float Tests isNan, isInf, isFinite, isNormal, signbit
Integer Ops add, sub, mul, divTrunc, divFloor, divCeil
Bit Ops shl, shr, rotl, rotr, log2_int, isPowerOfTwo
Comparison order, compare, clamp, sign

Mathematical Constants

const std = @import("std");
const math = std.math;

// Fundamental constants
const euler = math.e;              // 2.71828...
const pi_val = math.pi;            // 3.14159...
const golden = math.phi;           // 1.61803... (golden ratio)
const tau_val = math.tau;          // 2 * pi

// Logarithmic constants
const log2_e = math.log2e;         // log2(e)
const log10_e = math.log10e;       // log10(e)
const ln_2 = math.ln2;             // ln(2)
const ln_10 = math.ln10;           // ln(10)

// Square root constants
const sqrt_2 = math.sqrt2;         // sqrt(2)
const inv_sqrt2 = math.sqrt1_2;    // 1/sqrt(2)

// Angle conversion
const rad_deg = math.rad_per_deg;  // pi/180
const deg_rad = math.deg_per_rad;  // 180/pi

Angle Conversion

// Convert between radians and degrees
const radians = std.math.degreesToRadians(@as(f32, 90.0));  // pi/2
const degrees = std.math.radiansToDegrees(@as(f32, std.math.pi));  // 180.0

// Works with vectors
const angles: @Vector(3, f32) = .{ 90.0, 180.0, 270.0 };
const rads = std.math.degreesToRadians(angles);

Trigonometric Functions

const x: f32 = std.math.pi / 4.0;

// Basic trig (use hardware instructions when available)
const sine = std.math.sin(x);       // 0.7071...
const cosine = std.math.cos(x);     // 0.7071...
const tangent = std.math.tan(x);    // 1.0

// Inverse trig
const asin_val = std.math.asin(@as(f32, 0.5));  // pi/6
const acos_val = std.math.acos(@as(f32, 0.5));  // pi/3
const atan_val = std.math.atan(@as(f32, 1.0));  // pi/4
const atan2_val = std.math.atan2(@as(f32, 1.0), @as(f32, 1.0));  // pi/4

// Hyperbolic functions
const sinh_val = std.math.sinh(x);
const cosh_val = std.math.cosh(x);
const tanh_val = std.math.tanh(x);
const asinh_val = std.math.asinh(x);
const acosh_val = std.math.acosh(@as(f32, 2.0));
const atanh_val = std.math.atanh(@as(f32, 0.5));

Exponential and Logarithmic Functions

const x: f64 = 2.0;

// Exponential
const exp_val = std.math.exp(x);      // e^x
const exp2_val = std.math.exp2(x);    // 2^x
const expm1_val = std.math.expm1(x);  // e^x - 1 (more precise near 0)

// Logarithms
const log_val = std.math.log(f64, std.math.e, x);  // natural log
const log2_val = std.math.log2(x);     // log base 2
const log10_val = std.math.log10(x);   // log base 10
const log1p_val = std.math.log1p(x);   // ln(1 + x) (more precise near 0)

// Integer logarithms (for integer types)
const log2_int_val = std.math.log2_int(u32, 8);   // 3 (floor)
const log2_ceil = std.math.log2_int_ceil(u32, 9); // 4 (ceil)
const log10_int_val = std.math.log10_int(1000);   // 3

Power and Root Functions

// Powers
const pow_val = std.math.pow(f64, 2.0, 3.0);  // 2^3 = 8.0
const powi_val = try std.math.powi(i32, 2, 3); // checked integer power

// Roots
const sqrt_val = std.math.sqrt(@as(f64, 16.0));  // 4.0
const cbrt_val = std.math.cbrt(@as(f64, 27.0));  // 3.0

// Hypotenuse (sqrt(x^2 + y^2), avoids overflow)
const hyp = std.math.hypot(@as(f64, 3.0), @as(f64, 4.0));  // 5.0

Rounding Functions

const x: f32 = 2.7;

const floor_val = std.math.floor(x);  // 2.0 (toward -inf)
const ceil_val = std.math.ceil(x);    // 3.0 (toward +inf)
const trunc_val = std.math.trunc(x);  // 2.0 (toward zero)
const round_val = std.math.round(x);  // 3.0 (nearest, ties away from zero)

Floating-Point Classification

const x: f32 = 1.0;
const inf_val = std.math.inf(f32);
const nan_val = std.math.nan(f32);

// Classification tests
const is_nan = std.math.isNan(nan_val);           // true
const is_inf = std.math.isInf(inf_val);           // true
const is_pos_inf = std.math.isPositiveInf(inf_val);  // true
const is_neg_inf = std.math.isNegativeInf(-inf_val); // true
const is_finite = std.math.isFinite(x);           // true
const is_normal = std.math.isNormal(x);           // true

// Sign operations
const has_neg_sign = std.math.signbit(-1.0);      // true
const copied = std.math.copysign(@as(f32, 5.0), @as(f32, -1.0));  // -5.0

Float Properties

// Get float type properties
const mantissa_bits = std.math.floatMantissaBits(f32);  // 23
const exponent_bits = std.math.floatExponentBits(f32);  // 8
const eps = std.math.floatEps(f32);              // ~1.19e-7
const min_val = std.math.floatMin(f32);          // smallest positive normal
const max_val = std.math.floatMax(f32);          // largest finite
const true_min = std.math.floatTrueMin(f32);     // smallest positive (including subnormal)

// Special values
const inf_val = std.math.inf(f32);               // positive infinity
const nan_val = std.math.nan(f32);               // quiet NaN
const snan_val = std.math.snan(f32);             // signaling NaN

Approximate Equality

const x: f32 = 1.0;
const y: f32 = 1.0 + std.math.floatEps(f32);

// Absolute tolerance (good for values near zero)
const abs_eq = std.math.approxEqAbs(f32, x, y, 1e-6);

// Relative tolerance (good for larger values)
const rel_eq = std.math.approxEqRel(f32, x, y, std.math.sqrt(std.math.floatEps(f32)));

Integer Arithmetic with Overflow Checking

// These return errors on overflow instead of wrapping
const sum = std.math.add(i32, 2147483647, 1) catch |err| {
    // err is error.Overflow
    return err;
};

const product = std.math.mul(i32, 1000000, 1000000) catch |err| {
    return err;  // Overflow for i32
};

const diff = std.math.sub(u32, 5, 10) catch |err| {
    return err;  // Overflow (underflow) for unsigned
};

// Negation with potential overflow
const negated = std.math.negate(@as(i8, -128)) catch |err| {
    return err;  // Can't represent 128 in i8
};

// Shift with overflow check
const shifted = std.math.shlExact(u8, 2, 7) catch |err| {
    return err;  // Overflow: 2 << 7 doesn't fit in u8
};

The shift amount has type std.math.Log2Int(T), so an out-of-range count such as 8 for u8 is rejected before shlExact can report value overflow.

Division Functions

// Division toward zero
const trunc_div = try std.math.divTrunc(i32, -7, 3);  // -2

// Division toward negative infinity
const floor_div = try std.math.divFloor(i32, -7, 3);  // -3

// Division toward positive infinity
const ceil_div = try std.math.divCeil(i32, 7, 3);     // 3

// Exact division (error if remainder)
const exact = try std.math.divExact(i32, 10, 5);      // 2
// std.math.divExact(i32, 10, 3) returns error.UnexpectedRemainder

// Modulo (always non-negative result)
const mod_val = try std.math.mod(i32, -5, 3);  // 1

// Remainder (can be negative)
const rem_val = try std.math.rem(i32, -5, 3);  // -2

Bit Operations

// Shift with truncation (no overflow, large shifts -> 0)
const shl_val = std.math.shl(u8, 0b11111111, 3);   // 0b11111000
const shr_val = std.math.shr(u8, 0b11111111, 3);   // 0b00011111

// Negative shift amounts reverse direction
const neg_shift = std.math.shl(u8, 0b11111111, -2);  // 0b00111111

// Rotation (unsigned integers only)
const rotl_val = std.math.rotl(u8, 0b00000001, 4);  // 0b00010000
const rotr_val = std.math.rotr(u8, 0b00010000, 4);  // 0b00000001

// Power of two checks
const is_pow2 = std.math.isPowerOfTwo(@as(u32, 8));   // true
const floor_pow2 = std.math.floorPowerOfTwo(u32, 65); // 64
const ceil_pow2 = try std.math.ceilPowerOfTwo(u32, 65); // 128

Integer Type Utilities

// Get min/max values of integer type
const max_i32 = std.math.maxInt(i32);  // 2147483647
const min_i32 = std.math.minInt(i32);  // -2147483648

// Log2Int: type for bit indices
const Log2U32 = std.math.Log2Int(u32);  // u5 (can hold 0-31)

// Smallest type fitting a range
const T = std.math.IntFittingRange(0, 100);  // u7
const S = std.math.IntFittingRange(-50, 50); // i7

// Byte-aligned integer type
const ByteAligned = std.math.ByteAlignedInt(u5);  // u8

Comparison and Ordering

// Get ordering between values
const ord = std.math.order(@as(i32, 5), @as(i32, 3));  // .gt
// ord is std.math.Order: .lt, .eq, or .gt

// Runtime comparison operator
const result = std.math.compare(@as(i32, 5), .gte, @as(i32, 3));  // true

// Clamp to range
const clamped = std.math.clamp(@as(i32, 15), @as(i32, 0), @as(i32, 10));  // 10

// Wrap to half-open interval [-r, r)
const wrapped = std.math.wrap(@as(i32, 270), @as(i32, 180));  // -90

Sign and Interpolation

// Get sign (-1, 0, or 1)
const s = std.math.sign(@as(i32, -42));  // -1

// Linear interpolation
const lerped = std.math.lerp(@as(f32, 0.0), @as(f32, 100.0), @as(f32, 0.25));  // 25.0

Type Casting

// Safe cast (returns null if doesn't fit)
const maybe: ?u8 = std.math.cast(u8, @as(i32, 300));  // null

// Lossy cast (clamps to representable range)
const clamped = std.math.lossyCast(u8, @as(i32, 300));   // 255
const from_float = std.math.lossyCast(i16, @as(f32, 70000.0)); // 32767

// Negate and cast to signed
const negated = try std.math.negateCast(@as(u32, 100));  // -100 as i32

Wide Multiplication

// Multiply without overflow (result is double width)
const wide = std.math.mulWide(u8, 200, 200);  // 40000 as u16

Complex Numbers

const Complex = std.math.Complex;

const z1 = Complex(f32).init(3.0, 4.0);  // 3 + 4i
const z2 = Complex(f32).init(1.0, 2.0);  // 1 + 2i

// Arithmetic
const sum = z1.add(z2);         // 4 + 6i
const diff = z1.sub(z2);        // 2 + 2i
const prod = z1.mul(z2);        // -5 + 10i
const quot = z1.div(z2);

// Operations
const conj = z1.conjugate();    // 3 - 4i
const neg = z1.neg();           // -3 - 4i
const recip = z1.reciprocal();
const mag = z1.magnitude();     // 5.0 (|z|)

// Multiply by i
const times_i = z1.mulbyi();    // -4 + 3i

// Complex math functions
const z_exp = std.math.complex.exp(z1);
const z_log = std.math.complex.log(z1);
const z_sin = std.math.complex.sin(z1);
const z_sqrt = std.math.complex.sqrt(z1);

Big Integers (Arbitrary Precision)

const big = std.math.big;
const Managed = big.int.Managed;

// Create big integers (requires allocator)
var a = try Managed.initSet(allocator, 12345678901234567890);
defer a.deinit();

var b = try Managed.initSet(allocator, 98765432109876543210);
defer b.deinit();

// Arithmetic
try a.add(&a, &b);
try a.mul(&a, &b);
var q = try Managed.init(allocator);
defer q.deinit();
var r = try Managed.init(allocator);
defer r.deinit();
try q.divTrunc(&r, &a, &b);  // quotient in q, remainder in r

// Comparison
const ord = a.order(b);  // .lt, .eq, or .gt

// Convert to primitive (if fits)
const val = a.toInt(i128) catch |err| {
    // Value doesn't fit in i128
    return err;
};

// Convert from string
var c = try Managed.init(allocator);
defer c.deinit();
try c.setString(10, "123456789012345678901234567890");

GCD and LCM

// Greatest common divisor
const gcd_val = std.math.gcd(@as(u32, 48), @as(u32, 18));  // 6

// Least common multiple
const lcm_val = std.math.lcm(@as(u32, 4), @as(u32, 6));  // 12
// lcm is not an error union; intermediate multiplication follows the selected
// integer overflow/safety behavior.

Gamma Functions

// Gamma function
const g = std.math.gamma(f64, 5.0);  // 24.0 (= 4!)

// Log gamma (more numerically stable for large values)
const lg = std.math.lgamma(f64, 100.0);

Notes

  • Floating-point support varies by function and target; check each signature rather than assuming every float width and comptime_float are accepted
  • Many functions support SIMD vectors: sin(@Vector(4, f32){...})
  • Integer helpers document their own error sets; error.Overflow and error.DivisionByZero are common examples, not a universal pair
  • Hardware instructions used when available (@sin, @cos, @sqrt, etc.)
  • approxEqAbs for values near zero, approxEqRel for larger values
  • Complex number operations available in std.math.complex
  • Big integers require allocator and are in std.math.big.int