Skip to content

Digit functions ​

Most rules compare whole numbers. Sometimes you need to talk about the digits inside a number instead: "the leading digit must not be 9", "the result must end in 0", "no digit may be a 5". Five built-in functions cover that.

All five operate on the absolute value of their argument. The sign is ignored, so digits(-47) and digits(47) are identical.

The five functions ​

FunctionReturnsExample
digits(x)Array of all digits, most significant firstdigits(123) → [1, 2, 3]
highest_digit(x)The most significant (leading) digithighest_digit(123) → 1
lowest_digit(x)The least significant (trailing) digitlowest_digit(123) → 3
number_of_digits(x)Count of digitsnumber_of_digits(123) → 3
digitAt(x, place)The digit at the named decimal placedigitAt(123, 10) → 2

Four of them take exactly one argument. digitAt takes exactly two. Passing the wrong number of arguments is an error, and so is calling a function with no arguments at all.

Using them ​

A function call can appear anywhere a number can — on either side of a comparison, inside IN, or as an argument to another function.

highest_digit(prev_sum) < 5         -- leading digit of the running total is under 5
lowest_digit(current_value) = 0     -- candidate must end in 0
number_of_digits(current_sum) <= 2  -- result must be at most two digits

The argument does not have to be a bare variable. Arithmetic is allowed inside it:

highest_digit(prev_sum + current_value) != 9

And the result can be tested with IN / NOT IN just like any other number:

highest_digit(current_value) IN (1, 3, 5, 7, 9)   -- leading digit must be odd
lowest_digit(current_sum) NOT IN (0, 5)           -- result must not end in 0 or 5

digits and quantifiers ​

digits(x) is the odd one out: it returns an array, not a number, so it is not useful in a plain comparison. Its job is to feed a quantifier, which applies a condition to each digit in turn:

EVERY d IN digits(prev_sum): d < 9    -- no nines anywhere in the running total
SOME d IN digits(current_value): d = 0  -- at least one digit of the candidate is zero

There is a shorthand for exactly this shape — ALL_DIGITS(x) and SOME_DIGITS(x). Both are covered on the quantifiers page.

Leading digit vs. highest place

highest_digit returns the value of the leading digit, not the place it sits in. For 1234 it returns 1, not 1000. If you want to know which decimal place a step affects, that is place, not a digit function.

Edge cases ​

These follow from how the functions are implemented, and are worth knowing before a rule surprises you:

ExpressionResultWhy
digits(0)[0]Zero has one digit
number_of_digits(0)1Same reason
highest_digit(-70)7Sign is stripped first
lowest_digit(-70)0Sign is stripped first
digitAt(47, 100)0Places above the number read as 0

digitAt in brief ​

digitAt(value, place) reads the digit of value sitting at the decimal place you name — 1 for ones, 10 for tens, 100 for hundreds, and so on. Formally it is floor(abs(value) / place) reduced to its last digit.

digitAt(current_value, 10) = 4      -- the tens digit of the candidate is 4
digitAt(prev_sum, 100) IN (0, 5)    -- hundreds digit of the running total is 0 or 5

Unlike the derived variables place, formula and digitBefore, digitAt works on values that touch several decimal places at once — for current_value = 47 it happily reports 4 at the tens and 7 at the ones. That is its main reason to exist, and it is covered properly on the decimal places page. Read that page before reaching for digitAt: in most rules the derived variables are the shorter and clearer tool.

Reference and cookbook for mental-math rule and recipe authoring.