Skip to main content

Math Functions

Every math function returns null on null input. Functions with a restricted domain (e.g. math.sqrt of a negative number) return null rather than raising.

Overview​

GoalFunction
Absolute valuemath.abs(n)
Roundmath.ceil, math.floor, math.trunc, math.round, math.sign
Root, power, logmath.sqrt, math.hypot, math.pow, math.exp, math.log, math.ln, math.log10, math.log_base
Bounds and interpolationmath.min, math.max, math.clamp, math.lerp
Number formatting and predicatesnumber.format, number.to_base, number.from_base, number.to_roman, number.from_roman, number.is_integer, number.is_even, number.is_odd, number.is_positive
Trigmath.sin, math.cos, math.tan, math.asin, math.acos, math.atan, math.atan2
Unit conversionmath.degrees, math.radians
Constantsmath.pi(), math.e()
Randommath.random() / random()
Arithmetic+ - * / % ^

Rounding and absolute value​

FunctionBehaviour
math.abs(n)Absolute value — Int stays Int, Float stays Float
math.ceil(n)Round up to the nearest integer
math.floor(n)Round down
math.trunc(n)Drop the fractional part, toward zero
math.round(n[, digits[, mode]])Round to nearest; default mode is half_up
math.sign(n)-1, 0, or 1
RETURN math.abs(-3.5);    // 3.5
RETURN math.ceil(3.1);    // 4
RETURN math.floor(3.9);   // 3
RETURN math.trunc(-3.9);  // -3
RETURN math.round(3.5);   // 4
RETURN math.round(2.5);   // 3       (default half-up rounding)
RETURN math.sign(-9);     // -1
RETURN math.sign(0);      //  0
RETURN math.sign(9)      //  1

Rounding modes​

math.round(n) defaults to half-up rounding and returns an integer. Pass digits to round to a decimal place. Pass a mode string to choose another strategy: 'half_up', 'half_even', 'ceil', 'floor', or 'trunc'.

RETURN math.round(3.14159, 2);                  // 3.14
RETURN math.round(2.5);                         // 3
RETURN math.round(2.5, 0, 'half_even')         // 2.0

Roots, powers, logs​

FunctionDomainReturns
math.sqrt(n)n ≥ 0Float; null on negative input
math.hypot(a, b)numeric inputssqrt(a² + b²) without intermediate overflow
math.pow(a, b)numeric inputsa raised to b
math.log(n) / math.ln(n)n > 0Natural log; null out of domain
math.log10(n)n > 0Base-10 log; null out of domain
math.log_base(n, base)n > 0, base > 0, base != 1Logarithm in an arbitrary base
math.exp(n)anye^n
RETURN math.sqrt(16);       // 4.0
RETURN math.hypot(3, 4);    // 5.0
RETURN math.pow(2, 10);     // 1024.0
RETURN math.sqrt(-1);       // null
RETURN math.log(math.e());       // 1.0
RETURN math.log10(1000);    // 3.0
RETURN math.log_base(8, 2); // 3.0
RETURN math.exp(1)         // 2.7182818…

Hypotenuse​

WITH 3 AS a, 4 AS b
RETURN math.hypot(a, b)   // 5.0

For 2D Cartesian points, geo.distance does this.

Bounds and Interpolation​

math.min(a, b, ...) and math.max(a, b, ...) compare numeric arguments inside a single row. They are scalar helpers, not aggregate functions. Use min(expr) and max(expr) when you need the smallest or largest value across rows in a group.

All arguments must be finite numbers. A null, string, map, list, or non-finite float returns null.

RETURN math.min(3, 1, 2);      // 1
RETURN math.max(3, 1, 2);      // 3
RETURN math.min(3, 1.5, 2);    // 1.5
RETURN math.max(3, null);      // null
RETURN math.clamp(125, 0, 100); // 100
RETURN math.lerp(10, 20, 0.25) // 12.5

math.clamp(x, lo, hi) constrains a value to an inclusive range. math.lerp(a, b, t) returns a + (b - a) * t, which is useful for normalising scores and building weighted formulas.

Number Formatting And Predicates​

The number.* namespace contains numeric presentation helpers and integer predicates. Base conversion supports radices from 2 through 36. Invalid digits, unsupported bases, and overflow return null.

RETURN number.format(12345.678, 2, ',');  // '12,345.68'
RETURN number.to_base(255, 16);           // 'ff'
RETURN number.from_base('ff', 16);        // 255
RETURN number.to_roman(1994);             // 'MCMXCIV'
RETURN number.from_roman('MCMXCIV')      // 1994

Predicates are intentionally type-aware:

RETURN number.is_integer(42);     // true
RETURN number.is_integer(42.5);   // false
RETURN number.is_even(42);        // true
RETURN number.is_odd(42);         // false
RETURN number.is_even(42.0);      // null
RETURN number.is_positive(0.1);   // true
RETURN number.is_negative(-1);    // true
RETURN number.is_zero(0.0);       // true
RETURN number.is_nan(0.0 / 0.0)  // true

number.is_even and number.is_odd accept integer values. Use number.is_integer first when data may arrive as floats. Sign predicates accept both integers and floats.

Trigonometry​

All angles are in radians. Domain violations (e.g. math.asin(2)) return null rather than raising.

FunctionNotes
math.sin(x), math.cos(x), math.tan(x)Standard trig
math.asin(x), math.acos(x), math.atan(x)Inverse trig; domain [-1, 1] for asin/acos
math.atan2(y, x)Two-argument arctangent — y first
math.degrees(r) / math.radians(d)Unit conversion
RETURN math.sin(math.pi() / 2);          // 1.0
RETURN math.cos(0);                 // 1.0
RETURN math.tan(math.pi() / 4);          // 0.9999…    (float rounding)
RETURN math.asin(1);                // 1.5707… (π/2)
RETURN math.asin(2);                // null       (out of domain)
RETURN math.atan2(1, 1);            // 0.7853… (π/4)
RETURN math.degrees(math.pi());          // 180.0
RETURN math.radians(180)           // 3.1415…

Work in degrees​

Most geometry inputs are degrees — wrap every trig call:

WITH 30 AS deg
RETURN math.sin(math.radians(deg)),
     math.cos(math.radians(deg))

Bearing between two points​

WITH 4.89 AS lon1, 52.37 AS lat1,
   4.40 AS lon2, 51.00 AS lat2
WITH math.radians(lat1) AS phi1, math.radians(lat2) AS phi2,
   math.radians(lon2 - lon1) AS d_lambda
RETURN (math.degrees(math.atan2(
math.sin(d_lambda) * math.cos(phi2),
math.cos(phi1) * math.sin(phi2) - math.sin(phi1) * math.cos(phi2) * math.cos(d_lambda)
)) + 360) % 360 AS bearing

Approximation — use geo.distance for real geodesic distance between WGS-84 points.

Constants​

RETURN math.pi();     // 3.14159…
RETURN math.e()      // 2.71828…

Random​

math.random() returns a Float in [0, 1). The bare random() form is an alias for the same function.

RETURN math.random();                    // e.g. 0.42389…
RETURN math.random() * 100;              // a Float in [0, 100)
RETURN toInteger(math.random() * 100)   // an Int in [0, 99]

Not cryptographically secure — seeded from system-time nanoseconds. Do not use for key generation or sampling that must be unpredictable.

Random sample​

Each row gets its own math.random() value:

MATCH (u:User)
RETURN u
ORDER BY math.random()
LIMIT 100

Weighted pick​

MATCH (p:Prize)
WITH p, math.random() * p.weight AS score
ORDER BY score DESC
LIMIT 1
RETURN p

Arithmetic operators​

OpExampleNotes
+a + bAlso concatenates strings and lists
-a - bUnary -x allowed
*a * b
/a / bTwo integers divide as integers, truncating toward zero; a Float operand gives a Float. Integer divide by zero fails the query; float divide by zero → null
%a % bRemainder, signed like the dividend; a Float operand gives a Float. Integer mod by zero fails the query; float mod by zero → null
^a ^ bExponent; an integral result that fits in Int comes back as Int
RETURN 10 / 3;           // 3         (integer division)
RETURN 10 / 4.0;         // 2.5
RETURN 10 % 3;           // 1
RETURN 7.5 % 2;          // 1.5
RETURN 2 ^ 10;           // 1024
RETURN 1 / 0            // error: integer division by zero

+, -, *, /, % and unary - on integers are overflow-checked: a result outside the 64-bit range fails the query instead of wrapping. See Integer overflow.

Mixed-type arithmetic​

RETURN 1 + 2.5;          // 3.5 (Float)
RETURN 10 / 3.0         // 3.333…

Any Float operand promotes the result to Float. For an exact quotient of two integers, make one of them a float:

RETURN toFloat(10) / 3      // 3.333…

Numeric precedence​

Unary -/+ binds tightest, then ^, then * / / / %, then binary + / -, then comparisons. Use parentheses freely.

RETURN 1 + 2 * 3;        // 7
RETURN (1 + 2) * 3;      // 9
RETURN -2 ^ 2;           // 4         (binds as (-2)^2)
RETURN -(2 ^ 2)         // -4

Common patterns​

Clamp a value​

WITH $raw AS raw, 0 AS lo, 100 AS hi
RETURN math.clamp(raw, lo, hi) AS clamped

Normalise to 0..1​

MATCH (m:Metric)
WITH min(m.value) AS lo, max(m.value) AS hi
MATCH (m:Metric)
RETURN m.id,
     (m.value - lo) / (hi - lo) AS normalised

Bucket a number​

MATCH (p:Product)
RETURN (p.price / 10) * 10 AS bucket, count(*) AS n
ORDER BY bucket

Log-scale bucket​

MATCH (p:Post)
WITH p, CASE WHEN p.views > 0 THEN toInteger(math.log10(p.views)) ELSE 0 END AS decade
RETURN decade, count(*) AS posts
ORDER BY decade

Exponential decay (recency score)​

MATCH (p:Post)
WITH p, temporal.between(p.published_at, temporal.now()).days AS age
RETURN p.id, p.title,
     p.views * math.exp(-age * 0.05) AS score
ORDER BY score DESC
LIMIT 20

Half-life of roughly 14 days at 0.05 — tune the constant to taste.

Percent change​

MATCH (m:Metric)
RETURN m.id,
     m.current,
     m.previous,
     CASE
       WHEN m.previous = 0 OR m.previous IS NULL THEN null
       ELSE (m.current - m.previous) * 100.0 / m.previous
     END AS pct_change

Guard against zero/null — see CASE.

Weighted average​

MATCH (r:Review)
RETURN toFloat(sum(r.stars * r.weight)) / sum(r.weight) AS weighted_mean

Edge cases​

Integer overflow​

Integer +, -, *, /, % and unary - are checked, and so are list.sum, list.product and list.scan. A result outside the i64 range fails the query with a LORA_VALIDATION error such as integer addition overflowed — it never wraps or panics:

RETURN 9223372036854775807 + 1
// error: integer addition overflowed

For potentially huge inputs, coerce to Float with toFloat first.

NaN / Infinity​

Float division by zero never produces Infinity or NaN — it returns null (integer division by zero fails the query). Math functions guard their domains the same way.

RETURN 1.0 / 0.0;          // null
RETURN 0.0 / 0.0;          // null
RETURN math.sqrt(-1)           // null   (Cypher-level domain guard, not NaN)

If a NaN value does reach a query, IEEE 754 applies: NaN is neither less than nor greater than any value, and NaN == NaN is false.

Integer division truncates​

RETURN 7 / 2;                  // 3
RETURN -7 / 2;                 // -3
RETURN 7 / 2.0                 // 3.5

Make one operand a float (toFloat) when you want the exact quotient.

Limitations​

  • Integer +, -, *, % and unary - fail the query on overflow instead of wrapping. Coerce to Float with toFloat on potentially huge inputs. list.sum over integers is not overflow-checked.
  • NaN and Infinity follow IEEE 754 — NaN == NaN evaluates to false, and NaN is neither less nor greater than any other value.
  • math.random() / random() is not cryptographically secure.

See also​