Skip to content
CryoCryo home
StdlibNumerics

math

Numeric operations on floats and integers. math is a single-file module with no sub-modules, so import std::math; brings in the whole thing.

Naming follows C: cbrt, ln, sin, atan2. min and max are a single generic over any cmp::Ord type rather than a family of type-suffixed names. The transcendentals are f64-only, because libm is.

Operations that belong to one numeric type are methods of that type, not functions here: x.sqrt() and x.abs() on f32 and f64, and x.abs(), x.checked_abs() and x.clamp(lo, hi) on every numeric primitive. See primitives.

Two implementation tiers

This distinction shows up in behaviour, so it is worth stating plainly.

Transcendentals — sin, exp, pow, and friends — are thin wrappers over libm and inherit its platform behaviour: rounding mode, errno on domain errors, subnormal handling. Cryo does not re-derive those IEEE-754 corner cases.

Classification and the cheap operations — floor, round, copysign, mul_add, and the sqrt and abs float methods — are Cryo-owned compiler intrinsics lowered to LLVM float intrinsics. Same results as the libm functions of the same name, but no libm dependency and no errno.

Constants

const PI:               f64 = 3.141592653589793;
const TAU:              f64 = 6.283185307179586;
const E:                f64 = 2.718281828459045;
const PHI:              f64 = 1.618033988749895;
const SQRT_2:           f64 = 1.4142135623730951;
const SQRT_3:           f64 = 1.7320508075688772;
const LN_2:             f64 = 0.6931471805599453;
const LN_10:            f64 = 2.302585092994046;
const LOG2_E:           f64 = 1.4426950408889634;
const LOG10_E:          f64 = 0.4342944819032518;
const F64_EPSILON:      f64 = 2.220446049250313e-16;
const F64_MAX:          f64 = 1.7976931348623157e308;
const F64_MIN:          f64 = -1.7976931348623157e308;
const F64_MIN_POSITIVE: f64 = 2.2250738585072014e-308;
const F32_EPSILON:      f32 = 1.1920929e-7;
const F32_MAX:          f32 = 3.4028235e38;
const F32_MIN:          f32 = -3.4028235e38;
const F32_MIN_POSITIVE: f32 = 1.1754944e-38;

*_MIN is the most-negative finite value (equal to -*_MAX); *_MIN_POSITIVE is the smallest positive normal. infinity(), neg_infinity(), and nan() produce the non-finite values.

function infinity() -> f64;

function neg_infinity() -> f64;

function nan() -> f64;

Powers and roots

FunctionNotes
powiInteger exponent.
hypotsqrt(x² + y²) without intermediate overflow.
mul_addFused a * b + c — one rounding, not two.

Square root is the sqrt method of f32 and f64: x.sqrt().

function cbrt(value: f64) -> f64;

function pow(base: f64, exponent: f64) -> f64;

function powi(base: f64, exp: i64) -> f64;

base raised to an integer power via binary exponentiation (libm has no powi). Negative exp returns the reciprocal; powi(_, 0) == 1.0, including powi(0.0, 0) == 1.0.

function hypot(x: f64, y: f64) -> f64;

sqrt(x^2 + y^2) without spurious overflow/underflow: libm's hypot scales by the larger operand, so hypot(3e200, 4e200) == 5e200 (the naive sqrt(x*x + y*y) overflows to +inf there).

function mul_add(a: f64, b: f64, c: f64) -> f64;

Fused multiply-add: a * b + c computed with a single rounding (IEEE fma), more accurate than performing the multiply and add separately.

Exponentials and logarithms

expm1 and log1p compute exp(x) - 1 and ln(1 + x) accurately for small x, where the naive forms lose most of their significant digits. log(value, base) takes an arbitrary base.

function exp(value: f64) -> f64;

function exp2(value: f64) -> f64;

function expm1(value: f64) -> f64;

exp(value) - 1, accurate for value near 0 (IEEE expm1).

function ln(value: f64) -> f64;

function log2(value: f64) -> f64;

function log10(value: f64) -> f64;

function log1p(value: f64) -> f64;

ln(1 + value), accurate for value near 0 (IEEE log1p).

function log(value: f64, base: f64) -> f64;

Logarithm of value in an arbitrary base.

Trigonometry

function sin(value: f64) -> f64;

function cos(value: f64) -> f64;

function tan(value: f64) -> f64;

function asin(value: f64) -> f64;

function acos(value: f64) -> f64;

function atan(value: f64) -> f64;

function atan2(y: f64, x: f64) -> f64;

function sinh(value: f64) -> f64;

function cosh(value: f64) -> f64;

function tanh(value: f64) -> f64;

function to_radians(degrees: f64) -> f64;

Convert degrees to radians and back.

function to_degrees(radians: f64) -> f64;

Rounding and sign

fract is the fractional part and fmod the floating-point remainder. A float's absolute value is its abs method, which clears the sign bit, so (-0.0).abs() is 0.0 and a negative NaN comes back positive.

function floor(value: f64) -> f64;

function ceil(value: f64) -> f64;

function round(value: f64) -> f64;

Round to the nearest integer, halfway cases away from zero (round(2.5) == 3.0, round(-2.5) == -3.0) - C round semantics, not the current-rounding-mode rint/nearbyint.

function trunc(value: f64) -> f64;

function fract(value: f64) -> f64;

Fractional part, with the sign of value (fract(-2.25) == -0.25).

function copysign(magnitude: f64, sign: f64) -> f64;

magnitude with the sign of sign (IEEE copysign).

function fmod(x: f64, y: f64) -> f64;

Floating-point remainder of x / y (IEEE fmod).

function signum(value: f64) -> f64;

+1.0 / -1.0 for non-zero values; preserves +0.0, -0.0, and NaN.

Comparison and clamping

min and max are generic over both floats and integers.

abs, checked_abs, and clamp are methods of each numeric primitive: x.abs(), x.clamp(lo, hi). checked_abs is the non-wrapping counterpart of abs: on a signed integer, the most-negative value has no positive counterpart in two's complement, so abs wraps to itself and checked_abs returns None. See the overflow contract for why there is no broader checked_* family.

function min<T>(a: T, b: T) -> T where T: cmp::Ord;

Smaller of a and b. NaN-transparent for the float types.

function max<T>(a: T, b: T) -> T where T: cmp::Ord;

Larger of a and b. NaN-transparent for the float types.

Classification

type enum FpCategory {
    Nan;
    Infinite;
    Zero;
    Subnormal;
    Normal;
}

These are implemented over the IEEE-754 bit pattern in pure Cryo, not through libm — the C equivalents are macros rather than exported symbols.

function is_nan(value: f64) -> boolean;

True if value is NaN (quiet or signaling).

function is_infinite(value: f64) -> boolean;

True if value is positive or negative infinity.

function is_finite(value: f64) -> boolean;

True if value is finite - neither NaN nor infinite. Includes +/-0 and subnormals.

function is_normal(value: f64) -> boolean;

True if value is a normal number: finite, nonzero, and not subnormal.

function is_sign_negative(value: f64) -> boolean;

True if value's IEEE-754 sign bit is set. Unlike the class tests this reports the raw sign bit, so it is true for -0.0 and negative NaNs too. A single bit read - no libm.

function classify(value: f64) -> FpCategory;

Classify value into its IEEE-754 category.

Integer helpers

next_power_of_two rounds up; align_up / align_down round to a power-of-two boundary. The pointer-flavoured versions of the alignment helpers live in core::mem.

function gcd(a: u64, b: u64) -> u64;

Greatest common divisor (Euclid). gcd(0, 0) == 0.

function lcm(a: u64, b: u64) -> u64;

Least common multiple. lcm(_, 0) == 0.

function is_power_of_two(value: u64) -> boolean;

True if value is a power of two (zero is not).

function next_power_of_two(value: u64) -> u64;

Smallest power of two >= value. next_power_of_two(0) == 1.

function align_up(value: u64, align: u64) -> u64;

Round value up to the next multiple of align. An align of 0 or 1 returns value unchanged. Used for memory/field layout where align is a power of two, but works for any non-zero alignment. Consolidates the align_up helper the compiler's type-layout pass hand-rolls.

function align_down(value: u64, align: u64) -> u64;

Round value down to the previous multiple of align. An align of 0 or 1 returns value unchanged.