The following defines \ifdecimal, an expandable, fastish test that works for arbitrarily big numbers and places. It works for both , and . as the decimal separator, and arbitrary many signs. Everything that has at least one digit, at most one decimal separator, and no spaces is considered a decimal number. The input is fully expanded then stringified, the result of which has to match the aforementioned rules.
\documentclass{article}
\makeatletter
% needed to get a space inside of a definition
\newcommand\exparg@o[2]{\expanded{\unexpanded{#1}\expandafter}\expandafter{#2}}
% the front facing macro
\newcommand\ifdecimal[1]
{%
\expandafter\ifdecimal@space\detokenize\expanded{{#1}}\STOP
\ifdecimal@true
}
% true and false branching
\let\ifdecimal@true@firstoftwo
\long\def\ifdecimal@false#1\ifdecimal@true#2#3{#3}
% check has no spaces
\exparg@o{\def\ifdecimal@space#1\STOP}%
{%
@firstofone{\ifdecimal@space@#1\STOP\ifdecimal@false} \STOP % <- space
\ifdecimal@sign#1\MARK,\MARK
}
\def\ifdecimal@space@#1 #2\STOP{}
% strip leading signs
\def\ifdecimal@sign#1%
{%
\ifdecimal@sign@#1-\ifdecimal@sign@true+#1\ifdecimal@sign@true
+-\ifdecimal@dot#1%
}
\def\ifdecimal@sign@#1+-{}
\def\ifdecimal@sign@true#1+-\ifdecimal@dot#2{\ifdecimal@sign}
% turn one , into a . and then remove one decimal marker
\def\ifdecimal@dot#1,{\ifdecimal@dot@#1.}
\def\ifdecimal@dot@#1.{\ifdecimal@healmark\ifdecimal@empty#1}
\def\ifdecimal@healmark#1\MARK#2\MARK{#1\STOP}
% check is non-empty
\def\ifdecimal@empty#1\STOP
{%
\ifdecimal@empty@\MARK#1\STOP\ifdecimal@false\MARK\STOP
% the {0 \ifdecimal@done} works as the right delimiter to the input argument
\ifdecimal@digits#1{0 \ifdecimal@done}%
}
\def\ifdecimal@empty@#1\MARK\STOP{}
% check only digits left
\def\ifdecimal@digits#1%
{%
\ifnum9<1#1
\else
\expandafter\ifdecimal@false
\fi
\ifdecimal@digits
}
\def\ifdecimal@done#1\ifdecimal@digits{\fi}
\let\MARK\relax
\let\STOP\relax
\makeatother
% simple tests whether the macro works
\newcommand\ASSERT[2]
{%
\begingroup
\edef\tmpa{#2}%
\def\tmpb{#1}%
\expandafter
\endgroup
\ifx\tmpa\tmpb
\else
\GenericError{}{Assertion failed for \detokenize{#2}}{}{}%
\fi
}
\ASSERT{true}{\ifdecimal{0.}{true}{false}}
\ASSERT{true}{\ifdecimal{.0}{true}{false}}
\ASSERT{true}{\ifdecimal{0.0}{true}{false}}
\ASSERT{true}{\ifdecimal{00}{true}{false}}
\ASSERT{true}{\ifdecimal{+-+-+-+-00}{true}{false}}
\ASSERT{false}{\ifdecimal{.}{true}{false}}
\ASSERT{false}{\ifdecimal{+}{true}{false}}
\ASSERT{false}{\ifdecimal{-}{true}{false}}
\ASSERT{false}{\ifdecimal{+-}{true}{false}}
\ASSERT{false}{\ifdecimal{+-.}{true}{false}}
\ASSERT{false}{\ifdecimal{0+}{true}{false}}
\ASSERT{false}{\ifdecimal{0.0.0}{true}{false}}
\ASSERT{false}{\ifdecimal{a}{true}{false}}
\newcommand\five{5}
\ASSERT{true}{\ifdecimal{\five}{true}{false}}
\ASSERT{true}{\ifdecimal{\five .\five}{true}{false}}
\ASSERT{true}{\ifdecimal{\empty .\five}{true}{false}}
\ASSERT{false}{\ifdecimal{\five .\relax}{true}{false}}
\stop
\SomePercis supposedly accepting? – user691586 May 20 '23 at 09:02