# Testing for number

How do I test if a given parameter is a number, in order to apply a different style? And if not, just ignore... Like:

\domorestuffifnumber{things} -> things
\domorestuffifnumber{123}    -> \emph{123}


Thanks.

• You accepted my answer pretty quickly. That might discourage others from providing better answers. It'd probably be best to wait a day (or at least several hours) before accepting an answer. – TH. Dec 15 '10 at 23:57
• I have a number testing code in my tikz-timing package. See the source code if you are interested. – Martin Scharrer May 21 '12 at 7:30
• – Peter Grill Mar 9 '16 at 22:51
• In many situations (La)TeX does form numbers by expanding expandable tokens. Thus your request implies also checking whether the tokens forming the "given parameter" at the stage of expansion form an algorithm which terminates at all and which does not trigger errors. This is the halting problem. – Ulrich Diez May 18 '19 at 8:42

Here's a slightly flawed, but slightly more generic thing than you're asking for.

\documentclass{article}
\usepackage{etoolbox}
\makeatletter
\newcommand\ifnumber[1]{%
\begingroup
\edef\temp{#1}%
\expandafter\ifstrempty\expandafter{\temp}
{\endgroup\@secondoftwo}
{\expandafter\ifnumber@i\temp\@nnil}%
}
\def\ifnumber@i#1#2\@nnil{%
\if-#1%
\ifstrempty{#2}
{\def\temp{X}}
{\def\temp{#2}}%
\else
\def\temp{#1#2}%
\fi
\afterassignment\ifnumhelper
\count@0\temp\relax\@nnil
\endgroup
}

\def\numrelax{\relax}%
\def\ifnumhelper#1\@nnil{%
\def\temp{#1}%
\ifx\temp\numrelax
\aftergroup\@firstoftwo
\else
\aftergroup\@secondoftwo
\fi
}
\makeatother

\newcommand\testnumber[1]{#1: \ifnumber{#1}{Number}{Not a number}\par}
\begin{document}
\def\foo{-55}
\testnumber{1234}
\testnumber{\foo}
\testnumber{-}
\testnumber{}
\testnumber{1}
\testnumber{1234abc}
\testnumber{abc1234}
\end{document}


It's slightly complicated by checking if the first token in the expansion of the argument is a -. Unfortunately, it does not work if the argument is a register. (It probably doesn't work in other cases too.)

But from the \ifnumber macro, you should easily be about to build what you want.

\newcommand\domorestuffifnumber[1]{\ifnumber{#1}{\emph{#1}}{#1}}

• It may be a feature not to work if the argument is a register: with an argument of \count@ 0, we wouldn't want \count@ to be unpacked. In a situation where we want to get a number at any cost, then the only non-expandable control sequences which are allowed are registers, so we can test \ifcat\relax and throw in a \the to unpack. – Bruno Le Floch May 2 '11 at 16:18
\documentclass{article}
\def\isnum#1{%
\if!\ifnum9<1#1!\else_\fi
\emph{#1}\else#1\fi}

\begin{document}

\isnum{dummy}
\isnum{123}

\end{document}


if #1 is a number we have \ifnum9<1xxx which s true and therefore empty which leads to \if!! which is also true and \emph{#1} is the output. In the other case we have (#1 mybe 0a) \ifnum9<10a which is true and leaves a. Therefore we compare \if!a which is wrong, the reason why now the else part the output is.

• That's pretty clever but it doesn't handle negative numbers and like mine, doesn't handle registers. – TH. Dec 15 '10 at 21:43
• why the underscore after the first else? – Yiannis Lazarides May 2 '11 at 15:27
• @Yiannis: Ah, a copy and past error from another macro where I used it in another way. That part is never reached, you can use it without else_ or with any other text. – user2478 May 2 '11 at 15:44
• I thought so! Your construction would also work with ifcat rather than the if. Very clever. – Yiannis Lazarides May 2 '11 at 17:27

A LuaTeX solution:

\documentclass{article}
\begin{document}

\directlua{ dofile("myluastuff.lua") }

\newcommand\domorestuffifnumber[1]{%
\directlua{
domorestuffifnumber("#1")
}}

\def\foo{-55}
\domorestuffifnumber{1234}
\domorestuffifnumber{\foo}
\domorestuffifnumber{-}
\domorestuffifnumber{}
\domorestuffifnumber{1}
\domorestuffifnumber{1234abc}
\domorestuffifnumber{abc1234}
\end{document}


and the myluastuff.lua file:

function domorestuffifnumber( arg )
if tonumber(arg) then
tex.sprint("\\emph{" .. arg .. "}")
else
tex.sprint(arg or "")
end
end


I think the solution is quite readable.

This is a somewhat late answer, but I am including it here for completeness. When TeX is expecting a number a trailing zero will be ignored if it is followed by another number. However, if the 0 is followed by a non-number it will stop the scanning and insert the letter in the stream. The macro that follows capitalizes on this fact. We set a counter this way within a box. If it is a number the input gets fully absorbed and the width of the box is zero. If it is not a number the box will contain the non-numbers and hence its width will be greater than zero. By testing for the width of the box we can know if the input was a number or not.

\documentclass{article}
\makeatletter
\def\isNum#1{%
\sbox\z@{\@tempcnta=0#1\relax}
\ifdim\wd0>\z@\relax\@latex@warning{Not a number!}\else is numeric\fi
}

\begin{document}
\isNum{13}

% Handles registers
\isNum{\the\@tempcnta}

%warning for not a number
\isNum{dummy}
\makeatother
\end{document}


Empty input is treated a zero, and this can be useful in many situations.

(Edit: simplified as per egreg's comments)

• \sbox\z@{\@tempcnta=\number0#1\relax} is just as good and doesn't require \add@zero and \numtest. Moreover, using \sbox is safer if color might be involved. Of course this tests only for non negative numbers. – egreg May 1 '11 at 21:59
• @egreg Thanks, I guess it is simpler and I like the idea of the sbox; problem it will fail on \isNum{\the\@tmpcnta} although I guess it can be fixed by an appropriate number of \expandafters. – Yiannis Lazarides May 1 '11 at 22:09
• also yours fails on \@tempcnta because of the @ without \makeatletter. The \expandafter in your code is useless, because TeX expands tokens when looking for a number (which it does after finding \@tempcnta). Indeed also \number in my code is redundant. – egreg May 1 '11 at 22:31
• @egreg I corrected the \makeatletter, I had it correct on my compversion and copied wrongly in the post. I will have another look at your suggestions for which I thank you and edit the code later. – Yiannis Lazarides May 1 '11 at 22:38
• Doesn't work if used for symbols in math mode. – Weijun Zhou Apr 6 '19 at 7:24

The xstring package provides two commands: \IfInteger and \IfDecimal. Each of them uses the syntax

\IfInteger{<value being tested>}{<result if true>}{<result if false>}


If you don't want to differentiate between integers and decimals, we could do

\documentclass{article}
\usepackage{xstring}

\newcommand{\DoStuff}[1]{\emph{#1}}

\newcommand{\IfNumber}[1]
{%
\IfInteger{#1}
{\DoStuff{#1}}
{%
\IfDecimal{#1}
{\DoStuff{#1}}
{#1}%
}%
}

\begin{document}

\IfNumber{123}

\IfNumber{123.123}

\IfNumber{abc}

\end{document}

• \IfDecimal is enough I think! – Say OL Jul 30 '15 at 4:25

I'd like to present another solution that is able to parse integer and floating point numbers. Usage of the main macro is

\ifnumber[<optional setup>]{<tokens>}{<true part>}{<false part>}


where <tokens> is the string to be tested whether it represents a number. <optional setup> is an optional list of definitions to modify the way numbers are parsed (see below). By default numbers are parsed as floating point numbers with optional signs.

Pros

• Pure LaTeX implementation, no extra libraries required.
• Provides some basic customization. Currently supported are
• enabling/disabling of parsing optional signs (\let\parsesign=\iftrue/\iffalse),
• enabling/disabling of parsing the floating point part (\let\parsefloat=\iftrue/\iffalse), and
• definition of tokens that represent +, - and . (\parse@num@minus, \parse@num@plus and \parse@num@point, respectively).
• The main parsing macro \parse@num is fully expandable, given that the setup macros are in scope.
• The parsing macros correspond to states in a DFA, so more extensions should be quite easy to implement.

Cons

• Register values cannot be passed directly to \ifnumber but have to be expanded via \the or \number before.
• Macros in the string are not expanded properly.

Full example:

\documentclass{article}

\makeatletter

% Some conditional tests
\def\@genericif#1{#1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}
\def\@ifdigit#1{\@genericif{\ifnum1<1\noexpand#1\relax}}
\def\@ifempty#1{\@genericif{\if\relax\detokenize{#1}\relax}}

% The main parsing macros
\def\parse@num#1{%
\@ifempty{#1}%
{\parse@num@false}%
{\@genericif{\parsesign}%
{\parse@num@sign#1{}\@end}%
{\parse@num@dig#1{}\@end}%
}%
}
% Parse sign
\def\parse@num@sign#1#2\@end{%
\@genericif{\ifx\parse@num@minus#1}%
{\@ifempty{#2}{\parse@num@false}{\parse@num@dig#2\@end}}%
{\@genericif{\ifx\parse@num@plus#1}%
{\@ifempty{#2}{\parse@num@false}{\parse@num@dig#2\@end}}%
{\parse@num@dig#1#2\@end}%
}%
}
% Parse first digit
\def\parse@num@dig#1#2\@end{%
\@ifdigit{#1}%
{\@ifempty{#2}{\parse@num@true}{\parse@num@digs#2\@end}}%
{\parse@num@false}%
}
% Parse optional following digits
\def\parse@num@digs#1#2\@end{%
\@ifdigit{#1}{%
\@ifempty{#2}%
{\parse@num@true}%
{\parse@num@digs#2\@end}%
}{%
\@genericif{\parsefloat}{%
\@genericif{\ifx\parse@num@point#1}%
{\@ifempty{#2}{\parse@num@false}{\parse@num@decs#2\@end}}%
{\parse@num@false}%
}{\parse@num@false}%
}%
}
% Parse decimal places
\def\parse@num@decs#1#2\@end{%
\@ifdigit{#1}{%
\@ifempty{#2}%
{\parse@num@true}%
{\parse@num@decs#2\@end}%
}{\parse@num@false}%
}

% User interface
\newcommand\ifnumber[4][]{%
\begingroup
\let\parsesign=\iftrue
\let\parsefloat=\iftrue
\let\parse@num@minus=-%
\let\parse@num@plus=+%
\let\parse@num@point=.%
#1%
\def\parse@num@true{\endgroup#3}%
\def\parse@num@false{\endgroup#4}%
\parse@num{#2}%
}

\makeatother

\begin{document}
\newcommand\testnum[2][]{%
\makebox[5em][l]{#2}%
\makebox[5em][l]{\ifnumber[#1]{#2}{yes}{no}}%
}
\ttfamily
default setting\par
\testnum{}
\testnum{0}\par
\testnum{1}
\testnum{12345}\par
\testnum{+}
\testnum{+123}\par
\testnum{-}
\testnum{-123}\par
\testnum{-0}
\testnum{+0}\par
\testnum{0.}
\testnum{0.123}\par
\testnum{-.1}
\testnum{-+123}\par
\testnum{-0.123}
\testnum{+0.123}\par
\testnum{abc}
\testnum{123abc}\par

\medskip
\string\parsesign\ = \string\iffalse\par
\testnum[\let\parsesign=\iffalse]{123.45}\par
\testnum[\let\parsesign=\iffalse]{+123.45}\par
\testnum[\let\parsesign=\iffalse]{-123.45}\par

\medskip
\string\parsefloat\ = \string\iffalse\par
\testnum[\let\parsefloat=\iffalse]{123}
\testnum[\let\parsefloat=\iffalse]{123.45}\par
\testnum[\let\parsefloat=\iffalse]{+123}
\testnum[\let\parsefloat=\iffalse]{+123.45}\par
\testnum[\let\parsefloat=\iffalse]{-123}
\testnum[\let\parsefloat=\iffalse]{-123.45}\par

\medskip
\makeatletter
\string\parse@num@point\ = ,\par
\testnum[\let\parse@num@point=,]{-123,45}
\testnum[\let\parse@num@point=,]{0.12}\par
\makeatother
\end{document}