To add to what Hendrik says, I think the overall point was that \numexpr
, \dimexpr
, etc. can be used in a full expansion context without leaving a stray \relax
or space:
\edef\example{\the\dimexpr 10 pt + 20 pt \relax}
gives \example
defined as 30pt
with no unexpected tokens. That is in many ways much 'neater' than the alternative of leaving the \relax
in place. The same argument does not apply to TeX's setting of registers as that is never expandable, so the issue does not arise.
(Of course, for a definitive answer you would need to ask the members of the NTS team who actually wrote this code.)
I see two cases where \the\numexpr...\relax
works, but \pdfstrcmp{}{...}
will blow up, excluding the obvious case where ...
is replaced by 0\relax\undefined
, terminating the \numexpr
prematurely.
TeX interprets `\a
as a number, without expanding \a
. Hence, \the\numexpr`\a\relax
expands to 97
(the character code of a
), whereas \pdfstrcmp{}{`\a}
blows up if \a
is not defined.
Using \protected
control sequences can also cause trouble, because those are forcefully expanded "from the left" in a \numexpr
, but will not be expanded by \pdfstrcmp
. Take for instance
\protected\def\gob#1{}
\the\numexpr 0\gob\undefined \relax
\pdfstrcmp{}{0\gob\undefined}
In the case of \numexpr
, \gob
is expanded and removes the \undefined
control sequence. In the second case, however, the \edef
-like expansion leaves the \protected
control sequence \gob
untouched, and goes on to expand \undefined
, which is, well, undefined.
The original goal I had was to define a macro which takes in an argument which can be either empty or an integer expression, and evaluates the integer expression or puts a default value in the case of an empty argument. It seemed illogical to perform expansion in the \numexpr
case but not for the emptyness test, and I was thinking of testing with \pdfstrcmp{}{...}
. That can't work. An uglier but more correct choice is the following:
\catcode`@=11
\def\evaluate#1{\expandafter\evaluate@\the\numexpr#1\z@\z@\relax}
\def\evaluate@#1\z@#2\relax{#1}
\evaluate{1+2+3}
\evaluate{\empty}
\evaluate{\@gobble\a}
\evaluate{`\a}
If the argument to \evaluate
is empty or expands to an empty argument, the \numexpr
expansion will go through all of it and reach the first \z@
, evaluating that to 0
(default value), then stop because \z@
does not make sense in an integer expression there. The auxiliary cleans up.
On the other hand, if the argument to \evaluate
is a correct integer expression, it is evaluated, and \numexpr
stops expanding when encountering the first \z@
, and the cleaning up macro removes both \z@
.
I just thought of a better way: "f
-expand" (expand fully from the left, stopping at the first non-expandable token, removing it in case it is a space) the argument before testing for emptyness:
\def\evaluate#1{\expandafter\evaluate@\expandafter{\romannumeral-`0#1}}
\def\evaluate@#1{\the\numexpr\ifcat X\detokenize{#1}X\z@\fi#1\relax}
If the argument is empty or will expand to become empty, \romannumeral-`0#1
expands to nothing, and the test in \evaluate@
is true, which means we insert \z@
(default value). Otherwise #1
is evaluated.
Best Answer
The four expression primitives (
\numexpr
,\dimexpr
,\glueexpr
,\muexpr
) can be used anywhere that a register of the same type can be. This means that\numexpr
produces integers,\dimexpr
produces dimensions,\glueexpr
produces glue expressions and\muexpr
produces muskip values. As such, something like the example in the questioncannot yield a real number as the result has to be a dimension. (As we'll see below, the result here is
6pt
followed by.3pt\relax
.) (I've added\the
here so you can typeset the result rather than having to use it in an assignment. I'll do that throughout.)In all cases, the formal syntax is that the expressions absorb tokens and carry out appropriate mathematics up to either a
\relax
or the first non-valid token in the input. This is easiest to see with\numexpr
, which must produce an integer. So for examplewill print
4pt
, which is made up of the result of the expression (4
) followed by input which cannot be part of the expression and thus terminates it. This is useful in comparisons, where the comparator will necessarily terminate the expressionIn the case of
\dimexpr
and\glueexpr
, the formal syntax requires that the input consists of a dimension (or glue value) followed by an optional multiplication factor, and not the other way around. The factor may itself be a numerical expression. Thus all ofare valid and give the expected results, but
is not valid and leads to an error.
Expressions may use the operators
+
,-
,*
and/
along with parenthesis(
/)
. Division takes place with rounding, and this applies to any number expressions inside dimension/glue expressions. Thus for example we getIn glue expressions the plus/minus parts do not need parenthesis to be affected by a factor as they are necessarily one entity. So for example
yields
10pt plus 2pt
.Within expressions, TeX will coerce other numerical types into the correct form in the same way as occurs when doing register assignment. So
will result in
65536
, i.e. the value of1pt
converted to scaled points and then coerced into an integer. With\glueexpr
here the stretch/shrink would be dropped. Going the other way, any\numexpr
inside a\dimexpr
or\glueexpr
will need appropriate units providing