Unary operators: ‘-’ (negating, i.e. changing the sign), ‘+’
(just for completeness; does nothing in expressions), ‘!’ (logical
not; this works only within if and while
requests).10 See below for the use of unary operators in motion requests.

The logical not operator,
as described above,
works only within if and while requests.
Furthermore, it may appear
only at the beginning of an expression,
and negates the entire expression.
Attempting to insert the ‘!’ operator
within the expression results in a
‘numeric expression expected’ warning. This
maintains compatibility
with old versions of troff.

Example:

.nr X 1
.nr Y 0
.\" This does not work as expected
.if (\n[X])&(!\n[Y]) .nop X only
.
.\" Use this construct instead
.if (\n[X]=1)&(\n[Y]=0) .nop X only

Extrema: ‘>?’ (maximum), ‘<?’ (minimum).

Example:

.nr x 5
.nr y 3
.nr z (\n[x] >? \n[y])

The register z now contains 5.

Scaling: (c;e). Evaluate e
using c as the default scaling indicator. If c is
missing, ignore scaling indicators in the evaluation of e.

Parentheses may be used as in any other language. However, in
gtroff they are necessary to ensure order of evaluation.
gtroff has no operator precedence; expressions are evaluated left
to right. This means that gtroff evaluates ‘3+5*4’ as if it
were parenthesized like ‘(3+5)*4’, not as ‘3+(5*4)’, as might
be expected.

For many requests that cause a motion on the page, the unary operators
‘+’ and ‘-’ work differently if leading an expression. They
then indicate a motion relative to the current position (down or up,
respectively).

Similarly, a leading ‘|’ operator indicates an absolute position.
For vertical movements, it specifies the distance from the top of the
page; for horizontal movements, it gives the distance from the beginning
of the input line.