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.