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.
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.
\documentclass{article}
\def\isnum#1{%
\if!\ifnum9<1#1!\fi%
\emph{#1}%
\else#1%
\fi}
\begin{document}
\isnum{dummy}
\isnum{123}
\end{document}
If #1 is a number, we have \ifnum9<1xxx, which is 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 is the output.
Caveat: Does not work for numbers > 999999999.
else_ or with any other text.
–
May 02 '11 at 15:44
ifcat rather than the if. Very clever.
– yannisl
May 02 '11 at 17:27
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}}
\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 02 '11 at 16:18
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\add@zero#1{0#1}
\def\isNum#1{%
\sbox\z@{\@tempcnta=0#1\relax}
%\setbox0\hbox{\expandafter\@tempcnta\expandafter\add@zero\numtest\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 01 '11 at 21:59
sbox; problem it will fail on \isNum{\the\@tmpcnta} although I guess it can be fixed by an appropriate number of \expandafters.
– yannisl
May 01 '11 at 22:09
\@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 01 '11 at 22:31
\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.
– yannisl
May 01 '11 at 22:38
tikzpicture one would have to include the \@tempcnta=0#1\relax inside of a \pgfinterruptpicture, \endpgfinterruptpicture} pair as otherwise any input is set to a box with width 0.
– S. Olafsson
Apr 15 '22 at 15:08
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.
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}
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
\let\parsesign=\iftrue/\iffalse),\let\parsefloat=\iftrue/\iffalse), and+, - and . (\parse@num@minus, \parse@num@plus and \parse@num@point, respectively).\parse@num is fully expandable, given that the setup macros are in scope.Cons
\ifnumber but have to be expanded via \the or \number before.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}
;, :, ,, ], or ), or preceeded by :, [, or(. This means that numbers do not get highlighted in situations such as range(3), if x > 1:, foo[2:4], or in languages with ; delimiter, x = 3;, etc. Would you have some suggestion on how to improve this?
– gilbertohasnofb
Nov 08 '20 at 13:02
tikz-timingpackage. See the source code if you are interested. – Martin Scharrer May 21 '12 at 07:30