According to the TeXbook's* description of the


construct on p. 209 (the emphasis is mine)

TeX will expand macros following \if until two unexpandable tokens are found. If either token is a control sequence, TeX considers it to have character code 256 and category code 16, unless the current equivalent of that control sequence has been \let equal to a non-active character token. In this way, each token specifies a (character code, category code) pair.

What if one, or both, of the unexpandable tokens is an active character, e.g.

\if\noexpand @ ...

What is an active character's effective character code in the "eyes" of an \if construct?

* 20th printing, Addison-Wesley 1991

2 Answers 2


If you suppress expansion with \noexpand then it acts like a command name so is a character code if it is let to a character,

If you suppress expansion with \string then \if sees a non-active token of catcode 12, with the character code of the original character.


\if\noexpand @X yes \else no\fi

\if\noexpand @ \string@ yes \else no\fi



yes no

as \noexpand tests equal to X here as it is \let to X note however that if the character is \let to a character as here, then it isn't expandable so the \noexpand isn't needed.

  • What if the active character isn't \let to anything?
    – Evan Aad
    Aug 15, 2017 at 11:34
  • @EvanAad it acts like a csname all non-character control sequences are equal to each other but not equal to any character Aug 15, 2017 at 11:35
  • Then why does the following manuscript outputs 'no'? \catcode`@=13\if @\relax yes\else no\fi\bye
    – Evan Aad
    Aug 15, 2017 at 11:38
  • @EvanAad you expanded the if before assigning the catcode Aug 15, 2017 at 11:40
  • 4
    put a space after the 13, it is expanding \if while trying to finish the number to assign the catcode \count0=13\ifx ab 2\else 4\fi assigns count0 to be 134 Aug 15, 2017 at 11:44

Be very careful! Your code is

\if\noexpand @ ...

The first line starts with \catcode, so TeX starts making an assignment. It finds the number `@ (an alphabetical constant), so the assignment is to the category code of @. Then = is basically ignored, because it's optional and now TeX wants to see a <number>.

It finds two digits, but the evaluation of the <number> only ends when a non-digit comes along, with macro expansion.

If you remove the %, the endline generates a space token and this is a non-digit, so the <number> TeX is looking for is determined to be 13 (and the space token will be ignored).

With the %, TeX finds \if, which is expandable, so TeX expands it. The expansion of \if is null, but first TeX expands tokens until finding two unexpandable ones and compares them; after the comparison it will return true or false.

The next token is \noexpand, which is itself expandable, so it's expanded; its expansion is null, but the next token, whatever it is, is temporarily made equivalent to \relax, if an expandable control sequence or the character itself if an active character, and this ends the lookup for the first unexpandable token.

The next token is @ which has not changed its category code yet, because the assignment is not over. Only after finding the next unexpandable token, in this case a space, TeX determines that the <number> it was still looking for is indeed 13 and will proceed to perform the assignment.

Check the following code, where groups are used to bound the change in category code so not to spoil the following tests.


\if\noexpand @\at \message{true}\else\message{false}\fi

\if @\at \message{true}\else\message{false}\fi

\if @\at \message{true}\else\message{false}\fi

The output on the terminal is

true true
! Undefined control sequence.
l.15 \if @
          \relax \message{true}\else\message{false}\fi

and only in the third test @ has become active before the \if test is performed.

If the third test is

\if \noexpand @\at \message{true}\else\message{false}\fi

then it will return true as well, because \noexpand @ will just return the character @ with no ability to expand.

Moral of the story: always leave a space after a constant.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .