Numeric Expressions (The GNU Troff Manual)

### 5.4 Numeric Expressions

A numeric expression evaluates to an integer: it can be as simple as a literal ‘`0`’ or it can be a complex sequence of register and string interpolations interleaved with measurements and operators.

GNU `troff` provides a set of mathematical and logical operators familiar to programmers—as well as some unusual ones—but supports only integer arithmetic.35 The internal data type used for computing results is usually a 32-bit signed integer, which suffices to represent magnitudes within a range of ±2 billion.36

Arithmetic infix operators perform a function on the numeric expressions to their left and right; they are `+` (addition), `-` (subtraction), `*` (multiplication), `/` (truncating division), and `%` (modulus). Truncating division rounds to the integer nearer to zero, no matter how large the fractional portion. Overflow and division (or modulus) by zero are errors and abort evaluation of a numeric expression.

Arithmetic unary operators operate on the numeric expression to their right; they are `-` (negation) and `+` (assertion—for completeness; it does nothing). The unary minus must often be used with parentheses to avoid confusion with the decrementation operator, discussed below.

Observe the rounding behavior and effect of negative operands on the modulus and truncating division operators.

    .nr T 199/100
    .nr U 5/2
    .nr V (-5)/2
    .nr W 5/-2
    .nr X 5%2
    .nr Y (-5)%2
    .nr Z 5%-2
    T=\n[T] U=\n[U] V=\n[V] W=\n[W] X=\n[X] Y=\n[Y] Z=\n[Z]
        ⇒ T=1 U=2 V=-2 W=-2 X=1 Y=-1 Z=1

The sign of the modulus of operands of mixed signs is determined by the sign of the first. Division and modulus operators satisfy the following property: given a dividend a and a divisor b, a quotient q formed by ‘`(a / b)`’ and a remainder r by ‘`(a % b)`’, then qb + r = a.

GNU `troff`’s scaling operator, used with parentheses as `(c;e)`, evaluates a numeric expression e using c as the default scaling unit. If c is omitted, scaling units are ignored in the evaluation of e. This operator can save typing by avoiding the attachment of scaling units to every operand out of caution. Your macros can select a sensible default unit in case the user neglects to supply one.

    .\" Indent by amount given in first argument; assume ens.
    .de Indent
    .  in (n;\\$1)
    ..

Without the scaling operator, the foregoing macro would, if called with a unitless argument, cause indentation by the `in` request’s default scaling unit (ems). The result would be twice as much indentation as expected.

GNU `troff` also provides a pair of operators to compute the extrema of two operands: `>?` (maximum) and `<?` (minimum).

    .nr slots 5
    .nr candidates 3
    .nr salaries (\n[slots] <? \n[candidates])
    Looks like we'll end up paying \n[salaries] salaries.
        ⇒ Looks like we'll end up paying 3 salaries.

Comparison operators comprise `<` (less than), `>` (greater than), `<=` (less than or equal), `>=` (greater than or equal), and `=` (equal). `==` is a synonym for `=`. When evaluated, a comparison is replaced with ‘`0`’ if it is false and ‘`1`’ if true. In the `roff` language, positive values are true, others false.

We can operate on truth values with the logical operators `&` (logical conjunction or “and”) and `:` (logical disjunction or “or”). They evaluate as comparison operators do.

A logical complementation (“not”) operator, `!`, works only within `if`, `ie`, and `while` requests. Furthermore, `!` is recognized only at the beginning of a numeric expression not contained by another numeric expression. In other words, it must be the “outermost” operator. Including it elsewhere in the expression produces a warning in the ‘`number`’ category (see Warnings), and its expression evaluates false. This unfortunate limitation maintains compatibility with AT&T `troff`. Test a numeric expression for falsity by comparing it to a false value.37

    .nr X 1
    .nr Y 0
    .\" This does not work as expected.
    .if (\n[X])&(!\n[Y]) .nop A: X is true, Y is false
    .
    .\" Use this construct instead.
    .if (\n[X])&(\n[Y]<=0) .nop B: X is true, Y is false
        error→ warning: expected numeric expression, got '!'
        ⇒ B: X is true, Y is false

The `roff` language has no operator precedence: expressions are evaluated strictly from left to right, in contrast to schoolhouse arithmetic. Use parentheses `(` `)` to impose a desired precedence upon subexpressions.

    .nr X 3+5*4
    .nr Y (3+5)*4
    .nr Z 3+(5*4)
    X=\n[X] Y=\n[Y] Z=\n[Z]
        ⇒ X=32 Y=32 Z=23

For many requests and escape sequences that cause motion on the page, the unary operators `+` and `-` work differently when leading a numeric expression. They then indicate a motion relative to the drawing position: positive is down in vertical contexts, right in horizontal ones.

`+` and `-` are also treated differently by the following requests and escape sequences: `bp`, `in`, `ll`, `lt`, `nm`, `nr`, `pl`, `pn`, `po`, `ps`, `pvs`, `rt`, `ti`, `\H`, `\R`, and `\s`. Here, leading plus and minus signs serve as incrementation and decrementation operators, respectively. To negate an expression, subtract it from zero or include the unary minus in parentheses with its argument. See Setting Registers, for examples.

A leading `|` operator indicates a motion relative not to the drawing position but to a boundary. For horizontal motions, the measurement specifies a distance relative to a drawing position corresponding to the beginning of the input line. By default, tab stops reckon movements in this way. Most escape sequences do not; `|` tells them to do so.

    Mind the \h'1.2i'gap.
    .br
    Mind the \h'|1.2i'gap.
    .br
    Mind the
    \h'|1.2i'gap.
        ⇒ Mind the             gap.
        ⇒ Mind the    gap.
        ⇒ Mind the             gap.

One use of this feature is to define macros whose scope is limited to the output they format.

    .\" underline word $1 with trailing punctuation $2
    .de Underline
    .  nop \\$1\l'|0\[ul]'\\$2
    ..
    Typographical emphasis is best used
    .Underline sparingly .

In the above example, ‘`|0`’ specifies a negative motion from the current position (at the end of the argument just emitted, `\$1`) to the beginning of the input line. Thus, the `\l` escape sequence in this case draws a line from right to left. A macro call occurs at the beginning of an input line;38 if the `|` operator were omitted, then the underline would be drawn at zero distance from the current position, producing device-dependent, and likely undesirable, results. On the ‘`ps`’ output device, it underlines the period.

For vertical motions, the `|` operator specifies a distance from the first text baseline on the page or in the current diversion,39 using the current vertical spacing.

    A
    .br
    B \Z'C'\v'|0'D
        ⇒ A D
        ⇒ B C

In the foregoing example, we’ve used the `\Z` escape sequence (see Page Motions) to restore the drawing position after formatting ‘`C`’, then moved vertically to the first text baseline on the page.

Escape sequence: `\B`'``anything``'`` ¶

  Interpolate 1 if anything is a valid numeric expression, and 0 otherwise. The delimiter need not be a neutral apostrophe; see Delimiters.

You might use `\B` along with the `if` request to filter out invalid macro or string arguments. See Conditionals and Loops.

    .\" Indent by amount given in first argument; assume ens.
    .de Indent
    .  if \B'\\$1' .in (n;\\$1)
    ..

A register interpolated as an operand in a numeric expression must have an Arabic format; luckily, this is the default. See Assigning Register Formats.

Because spaces separate arguments to requests, spaces are not allowed in numeric expressions unless the (sub)expression containing them is surrounded by parentheses. See Invoking Requests, and Conditionals and Loops.

    .nf
    .nr a 1+2 + 2+1
    \na
        error→ expected numeric expression, got a space
        ⇒ 3
    .nr a 1+(2 + 2)+1
    \na
        ⇒ 6

The `nr` request (see Setting Registers) expects its second and optional third arguments to be numeric expressions; a bare `+` does not qualify, so our first attempt got a warning.