UK TeX FAQ -- question 218

### Is the argument a number?

TeX's own lexical analysis doesn't offer the macro programmer terribly much support: while category codes will distinguish letters (or what TeX currently thinks of as letters) from everything else, there's no support for analysing numbers.

The simple-minded solution is to compare numeric characters with the characters of the argument, one by one, by a sequence of direct tests, and to declare the argument "not a number" if any character fails all comparisons:

    \ifx1#1
    \else\ifx2#1
    ...
    \else\ifx9#1
    \else\isanumfalse
    \fi\fi...\fi

which one would then use in a tail-recursing macro to gobble an argument. One could do slightly better by assuming (pretty safely) that the digits' character codes are consecutive:

    \ifnum`#1<`0 \isanumfalse
    \else\ifnum`#1>`9 \isanumfalse
         \fi
    \fi

again used in tail-recursion. However, these forms aren't very satisfactory: getting the recursion "right" is troublesome (it has a tendency to gobble spaces in the argument), and in any case TeX itself has mechanisms for reading numbers, and it would be nice to use them.

Donald Arseneau's cite package offers the following test for an argument being a strictly positive integer:

    \def\IsPositive#1{%
      TT\fi
      \ifcat_\ifnum0<0#1 _\else A\fi
    }

which can be adapted to a test for a non-negative integer thus:

    \def\IsNonNegative{%
      \ifcat_\ifnum9<1#1 _\else A\fi
    }

or a test for any integer:

    \def\gobble#1{}
    \def\gobbleminus{\futurelet\temp\gobm}
    \def\gobm{\ifx-\temp\expandafter\gobble\fi}
    \def\IsInteger#1{%
      TT\fi
      \ifcat_\ifnum9<1\gobbleminus#1 _\else A\fi
    }

but this surely stretches the technique further than is reasonable.

If we don't care about the sign, we can use TeX to remove the entire number (sign and all) from the input stream, and then look at what's left:

    \def\testnum#1{\afterassignment\testresult\count255=#1 \end}
    \def\testresult#1\end{\ifx\end#1\end\isanumtrue\else\isanumfalse\fi}

(which technique is due to David Kastrup). In a later thread on the same topic, Michael Downes offered:

    \def\IsInteger#1{%
      TT\fi
      \begingroup \lccode`\-=`\0 \lccode`+=`\0
        \lccode`\1=`\0 \lccode`\2=`\0 \lccode`\3=`\0
        \lccode`\4=`\0 \lccode`\5=`\0 \lccode`\6=`\0
        \lccode`\7=`\0 \lccode`\8=`\0 \lccode`\9=`\0
      \lowercase{\endgroup
        \expandafter\ifx\expandafter\delimiter
        \romannumeral0\string#1}\delimiter
    }

which relies on `\romannumeral` producing an empty result if its argument is zero.

All the complete functions above are designed to be used in TeX conditionals written "naturally" - for example:

    \if\IsInteger{<subject of test>}%
      <deal with integer>%
    \else
      <deal with non-integer>%
    \fi