Skip to content
Jennifer Programming Language

math - numeric functions and constants

Enable with use math;. The numeric toolbox: the everyday functions (arithmetic helpers, trigonometry, hyperbolics, exponentials and logarithms, combinatorics) plus the special functions a probability layer is built on (the error, gamma and beta functions and their regularized incomplete forms), and the constants math.PI, math.E, and math.TAU. The library is strict on undefined inputs - anything that would produce NaN or Infinity in IEEE arithmetic instead produces a positioned runtime error, so a domain slip (math.ln(0), math.asin(2)) is a catchable error, never a silent NaN.

jennifer
use io;
use math;

io.printf("%f\n", math.PI);                    # 3.141592653589793
io.printf("%d\n", math.abs(0 - 42));           # 42
io.printf("%d\n", math.min(3, 7));             # 3
io.printf("%f\n", math.sqrt(2));               # 1.4142135623730951
io.printf("%f\n", math.pow(2, 10));            # 1024.0
io.printf("%d\n", math.floor(3.7));            # 3
io.printf("%d\n", math.ceil(3.2));             # 4
io.printf("%d\n", math.round(2.5));            # 3 (half away from zero)

Functions

CallReturnsNotes
math.abs(x)same type as x|x|; int → int, float → float; errors on math.abs(MinInt64) (no representable result)
math.min(a, b)int or floatint+int → int; mixed → float
math.max(a, b)int or floatsame rule as min
math.sqrt(x)floaterrors on negative input
math.pow(x, y)floaterrors if the result would be NaN or Infinity
math.floor(x)inttoward -∞; accepts int (identity); errors if the result does not fit in a 64-bit int (NaN / Inf / out of range)
math.ceil(x)inttoward +∞; same int-range error as floor
math.round(x)inthalf-away-from-zero (math.round(2.5) = 3); same int-range error as floor

min/max follow the same numeric-promotion rule as +: same-type arguments return that type; any float involved produces a float.

Trigonometry and hyperbolics

Angles are in radians. Every function returns a float; the inverse functions error outside their domain (via the strict-NaN rule), and sinh / cosh error when they overflow to infinity.

CallReturnsNotes
math.sin(x) / math.cos(x) / math.tan(x)floatBasic trig.
math.asin(x) / math.acos(x)floatInverse; error outside [-1, 1].
math.atan(x)floatInverse tangent.
math.atan2(y, x)floatQuadrant-aware arctangent of y/x.
math.sinh(x) / math.cosh(x) / math.tanh(x)floatHyperbolic; sinh/cosh error on overflow.
math.asinh(x)floatInverse hyperbolic sine (all reals).
math.acosh(x)floatInverse; error for x < 1.
math.atanh(x)floatInverse; error outside (-1, 1).

Exponentials and logarithms

CallReturnsNotes
math.exp(x)floate^x; errors on overflow.
math.expm1(x)floate^x - 1, accurate for small x.
math.ln(x)floatNatural log; errors for x <= 0.
math.log10(x)floatBase-10 log; errors for x <= 0.
math.log2(x)floatBase-2 log; errors for x <= 0.
math.log1p(x)floatln(1 + x), accurate for small x; errors for x <= -1.
math.log(x, base)floatLog of x to an arbitrary base; errors for non-positive x / base or base 1.

Roots, magnitude, and sign

CallReturnsNotes
math.cbrt(x)floatReal cube root (handles negative x).
math.hypot(x, y)floatsqrt(x*x + y*y) without intermediate overflow.
math.sign(x)same type as x-1 / 0 / 1 with the sign of x (int → int, float → float).
math.trunc(x)intRound toward zero; int argument is the identity.

Combinatorics

Integer in, integer out; a result past int64 is a catchable overflow error (not a silent wrap), consistent with the language's integer arithmetic.

CallReturnsNotes
math.factorial(n)intn!; errors on negative n or overflow (n > 20).
math.comb(n, k)intBinomial coefficient nCr (exact); k > n is 0; errors negative n/k.
math.perm(n, k)intk-permutations of n (nPr, exact); k > n is 0.
math.gcd(a, b)intGreatest common divisor (non-negative; gcd(0, 0) = 0).
math.lcm(a, b)intLeast common multiple (lcm(x, 0) = 0); errors on overflow.

Special functions

The functions a statistics / probability layer needs. All return a float. The error and gamma functions come from Go's standard library; the regularized incomplete gamma and beta - the engine every distribution CDF is built on - are computed by the standard series / continued-fraction algorithms, verified to machine precision on their convergent domain. Their results are pinned into [0, 1] (a CDF by definition), and an extreme argument that drives the series to a non-finite or non-converged result is a catchable error, not a silently-wrong value or a leaked NaN - the same strict contract the rest of the library keeps.

CallReturnsNotes
math.erf(x) / math.erfc(x)floatError function and its complement (erf + erfc = 1).
math.gamma(x)floatGamma function Γ(x); errors at the poles (0, negative integers) and on overflow.
math.lgamma(x)floatln|Γ(x)|, for range; errors at the poles.
math.beta(a, b)floatBeta function B(a, b) = Γ(a)Γ(b) / Γ(a+b); a, b > 0.
math.lbeta(a, b)floatln B(a, b), the stable log form; a, b > 0.
math.regGammaP(a, x)floatRegularized lower incomplete gamma P(a, x) in [0, 1] (the gamma / chi-square CDF); a > 0, x >= 0.
math.regGammaQ(a, x)floatUpper complement Q(a, x) = 1 - P(a, x).
math.regBetaI(x, a, b)floatRegularized incomplete beta I_x(a, b) in [0, 1] (the CDF engine for the beta, Student's t, F, and binomial); 0 <= x <= 1, a, b > 0.

Randomness

CallReturnsNotes
math.rand()floatuniform in [0, 1)
math.randInt(lo, hi)intuniform in [lo, hi] inclusive; errors if lo > hi
math.randSeed(n)nullreseeds the shared source deterministically

The library keeps one shared pseudo-random source. It is seeded from OS entropy at startup, so every run produces a different stream; call math.randSeed(n) when you need reproducible output (tests, demos, simulations). The same source drives lists.shuffle, so one randSeed call makes both deterministic together. (uuid and password draw from the crypto library's crypto-grade source instead, so randSeed does not affect them.)

The source is not cryptographically secure - it is Go's math/rand, seedable and predictable once observed. Don't derive secrets, tokens, keys, or anything security-sensitive from it.

jennifer
use io;
use math;

math.randSeed(42);                              # reproducible from here on
io.printf("%f\n", math.rand());                 # same value every run
io.printf("%d\n", math.randInt(1, 6));          # die roll, seeded

For secure random numbers, use crypto

When the value must be unpredictable - a password, a token, a nonce, a shuffle an adversary must not guess - use the crypto library's crypto-grade source instead of math. crypto.randInt(lo, hi) has the same shape as math.randInt (inclusive [lo, hi]), so it is a drop-in replacement; it is unseedable by design (there is no crypto.randSeed, since predictability is the thing you are avoiding):

jennifer
use io;
use crypto;

io.printf("%d\n", crypto.randInt(1, 6));        # secure, unbiased die roll
def token as bytes init crypto.randBytes(32);   # 256 bits of secure entropy

If you specifically want math's fast, lists.shuffle-sharing stream but seeded unpredictably (so each run differs and the seed can't be guessed), draw the seed from crypto once at startup:

jennifer
use math;
use crypto;

# a full-width unpredictable seed from the crypto-grade source
math.randSeed(crypto.randInt(0 - 9223372036854775807, 9223372036854775807));

# math.rand / math.randInt / lists.shuffle now run off an unguessable seed -
# fast and non-reproducible, but still NOT cryptographically secure output.

Note the trade-off: seeding math from crypto only makes the starting point unpredictable. The stream itself is still math/rand, so once enough output is observed it remains predictable - for output that must stay secret, use crypto.randInt / crypto.randBytes directly.

Constants

NameKindValue
math.PIfloat3.141592653589793...
math.Efloat2.718281828459045...
math.TAUfloat6.283185307179586... (2*PI, the full-turn constant)

Constants are referenced through the math. namespace prefix like every other library name; the bare identifiers PI and E are not in scope. With use math as m; the alias takes over (m.PI, m.E).

Strictness

The library refuses to produce floating-point edge values:

  • math.sqrt(-1) - undefined for negative input.
  • math.pow(0, -1) - division-by-zero territory; result would be Infinity.
  • math.pow(-1, 0.5) - would be NaN.

If a future use case needs the NaN/Infinity values, a math.NAN / math.INF constant (or dedicated check functions) can be added later. For now Jennifer treats them as errors at the boundary - consistent with how the language already refuses to silently coerce types.

See also: ../user-guide/index.md, ../technical/interpreter.md, index.md.