Test whether passed parameter is a number (possibly floating point) or dimension

If you don't mind the overhead of the pgfmath parser you can parse the number and check \ifpgfmathunitsdeclared. This is true if a TeX unit is specified at any point in the expression, or if the expression contains something that TeX regards has having units, such as skip, and box dimensions.

\documentclass[border=0.125cm]{standalone}

\usepackage{pgfmath,pgffor}
\parindent0pt

\def\print#1{\expandafter\Print#1@}
\def\Print#1{\if#1@\else\string#1\relax\expandafter\Print\fi}

\begin{document}

\newcount\mycount
\newdimen\mydimen
\newskip\myskip
\mycount=1
\mydimen=1pt
\myskip=1pt plus 1pt
\newbox\mybox
\setbox\mybox=\hbox{1}

\begin{minipage}{3in}
\foreach \value in {1, 1.0, 1e0, sin(1), 1cm, 1pt, 1mm, 1sp, 1mu, sin(1pt), 1+1pt,
    \mycount, \mydimen, \myskip, \wd\mybox, \mycount+1pt, width("1")}{%
    \pgfmathparse{\value}
    Expression \hbox to 2.5cm{\hfill`{\tt\print\value}'}
    \ifpgfmathunitsdeclared
        \emph{\bfseries has} a unit
    \else
        has no units
    \fi

}
\end{minipage}
\end{document}

enter image description here

It's also worth noting that there is a macro \pgfmathpostparse which is executed after the parser has finished but just before it exits in which further stuff an be done. Initially it is set to \relax but is advisable to check its value in case some library or other package changes it.

The result of the parse will be in \pgfmathresult and it is possible to change it (if one really wanted to). It is however still inside a TeX group so \global must be used if the result of some test is required.

The following example using a crude integer test is not great as the parser rarely returns integers (there are a few exceptions shown below) but illustrates how it can be used.

\documentclass[border=0.125cm]{standalone}
\usepackage{pgfmath,pgffor}
\parindent0pt

\makeatletter
\newif\ifpgfmathresultinteger
\def\pgfmathpostparse{%
    \expandafter\pgfutil@in@\expandafter.\expandafter{\pgfmathresult}%
    \ifpgfutil@in@%
        \global\pgfmathresultintegerfalse%
    \else%
        \global\pgfmathresultintegertrue%
    \fi%
}

\def\print#1{\expandafter\Print#1@}
\def\Print#1{\if#1@\else\string#1\relax\expandafter\Print\fi}


\begin{document}

\newcount\mycount
\newdimen\mydimen

\mycount=1
\mydimen=1pt

\begin{minipage}{3.5in}
\foreach \value in {1, int(1.0), \mycount, \mydimen, int(\mydimen+1pt), \mycount+1pt}{%
    \pgfmathparse{\value}
    Parsing \hbox to 3.5cm{\hfill`{\tt\print\value}'} 
    does
    \ifpgfmathresultinteger
    \else
        \emph{\bfseries not} 
    \fi
    give an integer

}
\end{minipage}

\end{document}

enter image description here


This is of course slow, but should take care of all the cases (number, dimension, wrong input).

\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\IfIsDim}{mmm}
 {
  \aellet_ifisdim:nnn { #1 } { #2 } { #3 }
 }

\tl_new:N \l_aellet_ifisdim_arg_tl
\bool_new:N \l_aellet_ifisdim_has_unit_bool
\regex_const:Nn \c_aellet_unit_regex { (pt|pc|in|bp|cm|mm|dd|cc|sp|em|ex|px)\s*\Z }
\regex_const:Nn \c_aellet_number_regex { \A\s*(\+|\-)*[0-9]*\.?[0-9]*\s*\Z }
\cs_new_protected:Npn \aellet_ifisdim:nnn #1 #2 #3
 {
  \tl_set:Nn \l_aellet_ifisdim_arg_tl { #1 }
  \regex_match:NnTF \c_aellet_unit_regex { #1 }
   {% there is a unit of measure at the end
    \bool_set_true:N \l_aellet_ifisdim_has_unit_bool
    \regex_replace_once:NnN \c_aellet_unit_regex { } \l_aellet_ifisdim_arg_tl
   }
   {% no unit of measure
    \bool_set_false:N \l_aellet_ifisdim_has_unit_bool
   }
  \regex_match:NVTF \c_aellet_number_regex \l_aellet_ifisdim_arg_tl
   {
    \bool_if:NTF \l_aellet_ifisdim_has_unit_bool { #2 } { #3 }
   }
   {
    \ERROR
   }
 }
\cs_generate_variant:Nn \regex_match:NnTF { NV }
\ExplSyntaxOff

\IfIsDim{3.5pt}{\typeout{IS DIM}}{\typeout{IS NUMBER}}
\IfIsDim{3.5}{\typeout{IS DIM}}{\typeout{IS NUMBER}}
\IfIsDim{-3}{\typeout{IS DIM}}{\typeout{IS NUMBER}}
\IfIsDim{+--.5}{\typeout{IS DIM}}{\typeout{IS NUMBER}}
\IfIsDim{ .5 }{\typeout{IS DIM}}{\typeout{IS NUMBER}}
\IfIsDim{3.5 in}{\typeout{IS DIM}}{\typeout{IS NUMBER}}
\IfIsDim{3.5pq}{\typeout{IS DIM}}{\typeout{IS NUMBER}}

\stop

Here's the output on the terminal

IS DIM
IS NUMBER
IS NUMBER
IS NUMBER
IS NUMBER
IS DIM
! Undefined control sequence.
<argument> \ERROR

l.41 ...pq}{\typeout{IS DIM}}{\typeout{IS NUMBER}}

?

Here is the way how PSTricks sets a length with or without a unit (run with tex):

\catcode`\@=11
\newdimen\psunit \psunit=10pt% the current unit, can be any value
\def\pstunit@off{\let\@psunit\ignorespaces\ignorespaces}
%
\def\pssetlength#1#2{%  #1: dimen  #2 value (unit)
  \let\@psunit\psunit
  \afterassignment\pstunit@off
  #1 #2\@psunit}

\pssetlength\psunit{2} \the\psunit   %  2 times of the current unit

\pssetlength\psunit{1cm} \the\psunit %  absolute 1cm, the new current unit and so on
\bye

It is really simple:

  • If #2 has no unit then the dimen #1 is set to #2\@psunit and the \afterassignment has no meaning because it is executed after the length setting.
  • If #2 has a unit then the dimen #1 is set to #2 and the following \@psunit is like a \ignorespaces; it was redefined by \afterassignment.