slang

math

Package math.

Markdown version · llms.txt · api.json

Constants, float functions, rounding, and checked integer arithmetic, compiled to C's <math.h> and compiler builtins: math.sqrt(x) is the instruction, not a call, and no function here is a safepoint.

import "math";

let r = math.sqrt(2.0);                  // 1.4142135623730951
let a = math.atan2(1.0, 1.0) * 4.0;      // math.pi
let x = math.clamp(v, 0.0, 1.0);
let n = math.round_to(3.14159, 2);       // 3.14
let i = math.floor_int(f) ?? 0;          // none for NaN, inf, out of range

// One function for every numeric type: T is the arguments' type.
let b: u8 = 250;
let s = math.checked_add(b, 10);         // opt[u8]: none, 260 does not fit
let w = math.saturating_add(b, 10);      // u8 255
let m = math.min(b, 100);                // u8: a literal takes b's type
let total = math.checked_mul(price, qty) ?? 0;   // money: overflow is none

math.div_floor(-7, 2);                   // -4, where -7 / 2 is -3
math.rem_euclid(-7, 3);                  // 2, never negative
math.gcd(48, 18);                        // 6
math.isqrt(99);                          // 9
math.mul_div(a, b, c);                   // a * b / c with no overflow in between
math.popcount(b);                        // 6, on b's own width
math.format_fixed(math.pi, 4);           // "3.1416"

Every numeric argument of a call shares one type, T, so math.abs of an i32 is an i32, and math.checked_add of two u8s is an opt[u8]. T is what a + b of the arguments would be, except that a literal takes the other arguments' type (math.min(b, 100) is a u8) and must fit it. Float functions make an integer argument float; integer functions reject a float. One name covers all ten numeric types because slang has no overloading.

  • Constants: pi, tau, e, phi, sqrt2, sqrt3, ln2, ln10, log2e, log10e, inf, neg_inf, nan, max_float, min_normal, min_subnormal, epsilon, f32_max, f32_min_normal, f32_epsilon, max_int, min_int, and max_/min_ of each fixed-width integer.
  • Any number: abs, min, max, clamp, sign.
  • Floats: is_nan, is_inf, is_finite, is_normal, signbit, copysign, nextafter, nextup, nextdown, ulp, frexp_mantissa, frexp_exponent, ldexp, ilogb, float_bits/float_from_bits, f32_bits/f32_from_bits, total_cmp (a total order, NaN included), approx_eq(a, b, rel_tol, abs_tol).
  • Rounding: floor, ceil, trunc, round (half away from zero), round_even (half to even), round_to(x, decimals), round_to_multiple, fract; floor_int, ceil_int, round_int, trunc_int give an opt[int].
  • Powers and logs: sqrt, cbrt, hypot, pow, powi(x, n), fma, fmod, remainder, mod_euclid, exp, exp2, expm1, log, log2, log10, log1p, log_base.
  • Trigonometry: sin … atanh, atan2, to_radians, to_degrees, wrap_angle (into (-pi, pi]), sin_pi/cos_pi (exact at integers and halves).
  • Special: gamma, lgamma, erf, erfc, erf_inv, beta, factorial, binomial.
  • Interpolation: lerp, inverse_lerp, remap, smoothstep, smootherstep.
  • Integers: checked_add/sub/mul/div/rem/neg/abs/pow/shl (an opt), saturating_add/sub/mul, wrapping_add/sub/mul/neg/pow, div_floor, div_ceil, div_euclid, rem_euclid, abs_diff, midpoint, isqrt, ilog2, ilog10, is_pow2, next_pow2, align_up, align_down, gcd, lcm, mul_div, mul_hi.
  • Bits: popcount, leading_zeros, trailing_zeros, bit_len, rotate_left, rotate_right, swap_bytes, reverse_bits.
  • Formatting: format(x, digits) (significant digits), format_fixed(x, decimals), format_sci(x, digits). These are the only functions here that allocate; format_fixed rounds an exact tie to even, as C's printf does (format_fixed(2.5, 0) is "2").

Floats follow IEEE 754: NaN propagates (through min, max and clamp too), -0.0 is kept and orders below 0.0, and no float function panics. An integer operation that can fail returns an opt: overflow, a zero divisor, MIN / -1. div_floor, div_ceil, div_euclid and rem_euclid panic on a zero divisor as / does, isqrt on a negative argument, and align_up/align_down on an alignment that is not a power of two. Programs link libm (-lm) whether or not they import math; on macOS that is libSystem.

math.mul_mod(a, b, m) is (a * b) mod m with the product held at twice the width, so modular arithmetic near the type's limit cannot overflow; math/numtheory is built on it.

API#

math.abs(T) -> T#
math.min(T, T) -> T#
math.max(T, T) -> T#
math.clamp(T, T, T) -> T#
math.sign(T) -> T#
math.is_nan(T) -> bool#
math.is_inf(T) -> bool#
math.is_finite(T) -> bool#
math.is_normal(T) -> bool#
math.signbit(T) -> bool#
math.copysign(T, T) -> T#
math.nextafter(T, T) -> T#
math.nextup(T) -> T#
math.nextdown(T) -> T#
math.ulp(T) -> T#
math.frexp_mantissa(T) -> T#
math.frexp_exponent(T) -> int#
math.ldexp(T, int) -> T#
math.ilogb(T) -> int#
math.float_bits(float) -> u64#
math.float_from_bits(int) -> float#
math.f32_bits(float) -> u32#
math.f32_from_bits(int) -> f32#
math.total_cmp(T, T) -> int#
math.approx_eq(T, T, T, T) -> bool#
math.floor(T) -> T#
math.ceil(T) -> T#
math.trunc(T) -> T#
math.round(T) -> T#
math.round_even(T) -> T#
math.round_to(T, int) -> T#
math.round_to_multiple(T, T) -> T#
math.fract(T) -> T#
math.floor_int(float) -> opt[int]#
math.ceil_int(float) -> opt[int]#
math.round_int(float) -> opt[int]#
math.trunc_int(float) -> opt[int]#
math.sqrt(T) -> T#
math.cbrt(T) -> T#
math.hypot(T, T) -> T#
math.pow(T, T) -> T#
math.powi(T, int) -> T#
math.fma(T, T, T) -> T#
math.fmod(T, T) -> T#
math.remainder(T, T) -> T#
math.mod_euclid(T, T) -> T#
math.exp(T) -> T#
math.exp2(T) -> T#
math.expm1(T) -> T#
math.log(T) -> T#
math.log2(T) -> T#
math.log10(T) -> T#
math.log1p(T) -> T#
math.log_base(T, T) -> T#
math.sin(T) -> T#
math.cos(T) -> T#
math.tan(T) -> T#
math.asin(T) -> T#
math.acos(T) -> T#
math.atan(T) -> T#
math.atan2(T, T) -> T#
math.sinh(T) -> T#
math.cosh(T) -> T#
math.tanh(T) -> T#
math.asinh(T) -> T#
math.acosh(T) -> T#
math.atanh(T) -> T#
math.to_radians(T) -> T#
math.to_degrees(T) -> T#
math.wrap_angle(T) -> T#
math.sin_pi(T) -> T#
math.cos_pi(T) -> T#
math.gamma(T) -> T#
math.lgamma(T) -> T#
math.erf(T) -> T#
math.erfc(T) -> T#
math.erf_inv(T) -> T#
math.beta(T, T) -> T#
math.factorial(int) -> float#
math.binomial(int, int) -> float#
math.lerp(T, T, T) -> T#
math.inverse_lerp(T, T, T) -> T#
math.remap(T, T, T, T, T) -> T#
math.smoothstep(T, T, T) -> T#
math.smootherstep(T, T, T) -> T#
math.checked_add(T, T) -> opt[T]#
math.checked_sub(T, T) -> opt[T]#
math.checked_mul(T, T) -> opt[T]#
math.checked_div(T, T) -> opt[T]#
math.checked_rem(T, T) -> opt[T]#
math.checked_neg(T) -> opt[T]#
math.checked_abs(T) -> opt[T]#
math.checked_pow(T, int) -> opt[T]#
math.checked_shl(T, int) -> opt[T]#
math.saturating_add(T, T) -> T#
math.saturating_sub(T, T) -> T#
math.saturating_mul(T, T) -> T#
math.wrapping_add(T, T) -> T#
math.wrapping_sub(T, T) -> T#
math.wrapping_mul(T, T) -> T#
math.wrapping_neg(T) -> T#
math.wrapping_pow(T, int) -> T#
math.div_floor(T, T) -> T#
math.div_ceil(T, T) -> T#
math.div_euclid(T, T) -> T#
math.rem_euclid(T, T) -> T#
math.abs_diff(T, T) -> u64#
math.midpoint(T, T) -> T#
math.isqrt(T) -> T#
math.ilog2(T) -> opt[int]#
math.ilog10(T) -> opt[int]#
math.is_pow2(T) -> bool#
math.next_pow2(T) -> opt[T]#
math.align_up(T, T) -> opt[T]#
math.align_down(T, T) -> T#
math.gcd(T, T) -> T#
math.lcm(T, T) -> opt[T]#
math.mul_div(T, T, T) -> opt[T]#
math.mul_hi(T, T) -> T#
math.mul_mod(T, T, T) -> opt[T]#
math.popcount(T) -> int#
math.leading_zeros(T) -> int#
math.trailing_zeros(T) -> int#
math.bit_len(T) -> int#
math.rotate_left(T, int) -> T#
math.rotate_right(T, int) -> T#
math.swap_bytes(T) -> T#
math.reverse_bits(T) -> T#
math.format(float, int) -> str#
math.format_fixed(float, int) -> str#
math.format_sci(float, int) -> str#