# Expansion in \numexpr…\relax versus \pdfstrcmp

The \numexpr...\relax construction in eTeX allows to evaluate numerical expressions, and it expands tokens fully as it goes.

The \pdfstrcmp{...}{...} construction in pdfTeX lets us compare two lists of tokens after full expansion and conversion to a string (with \detokenize).

Are there specific token lists (parameter-less macros) \foo such that \the\numexpr\foo\relax correctly produces an integer, but \pdfstrcmp{\foo}{} causes a TeX error? It seems that the expansion behaviour is the same in both cases, but one converts its argument to an integer, and the other one to a string.

-
\numexpr doesn't fully expand tokens in the same sense of \edef; it stops expanding as soon as it finds something that can't be interpreted as a numexpr; if this token happens to be \relax, it is swallowed. I'd say it's a particular case of what \pdfstrcmp does. –  egreg Dec 20 '11 at 8:00
@egreg: true. Note that I put the requirement that \the\numexpr\foo\relax correctly produces an integer. So \def\foo{1+2X\junk} is not eligible for instance. –  Bruno Le Floch Dec 20 '11 at 12:38

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.

1. 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.

2. 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.

-
Good catch about \a. –  egreg Dec 22 '11 at 21:12
@egreg Also, I realized now that when the default is 0, I can simply do \the\numexpr#1+\z@\relax. But I don't think that can be adapted to any other default. –  Bruno Le Floch Dec 22 '11 at 21:16
If #1 is a correct integer expression, isn't also #1+3 again a correct integer expression? –  egreg Dec 22 '11 at 21:25
@egreg yes, but I need {}=> default and {123}=>123, not 123+default. –  Bruno Le Floch Dec 22 '11 at 21:57
Bruno: What of \the\numexpr 0\gobb\undefined\relax`? –  Ahmed Musa Dec 23 '11 at 5:02