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:
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:\ifx1#1 \else\ifx2#1 ... \else\ifx9#1 \else\isanumfalse \fi\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.\ifnum`#1<`0 \isanumfalse \else\ifnum`#1>`9 \isanumfalse \fi \fi
Donald Arseneau's cite package offers the following test for an argument being a strictly positive integer:
which can be adapted to a test for a non-negative integer thus:\def\IsPositive#1{% TT\fi \ifcat_\ifnum0<0#1 _\else A\fi }
or a test for any integer:\def\IsNonNegative{% \ifcat_\ifnum9<1#1 _\else A\fi }
but this surely stretches the technique further than is reasonable.\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 }
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:
(which technique is due to David Kastrup). In a later thread on the same topic, Michael Downes offered:\def\testnum#1{\afterassignment\testresult\count255=#1 \end} \def\testresult#1\end{\ifx\end#1\end\isanumtrue\else\isanumfalse\fi}
which relies on\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 }
\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:
The LaTeX memoir class has an internal command of its own,\if\IsInteger{<subject of test>}% <deal with integer>% \else <deal with non-integer>% \fi
\checkifinteger{
num}
, that sets the conditional command
\ifinteger
according to whether the argument was an integer.
This question on the Web: http://www.tex.ac.uk/cgi-bin/texfaq2html?label=isitanum