14

Is there an easy way to test if a number is negative?

I don't need a general solution, but it does need to be able to handle a floating point value. I was using this solution from the TeX FAQ until I realized that it has problems with positive floating point numbers. Here is a test file

\documentclass{article}

\def\IsPositive#1{% TT\fi% \ifcat_\ifnum0<0#1 _\else A\fi% }

\newcommand{\PrintSignOfNumber}[1]{% \if\IsPositive{#1}% $#1 \ge 0$% \else% $#1 < 0$% \fi% }

\begin{document}

\PrintSignOfNumber{2}\par \PrintSignOfNumber{-2}

\PrintSignOfNumber{2.0}\par % reported as negative \PrintSignOfNumber{-2.0}

\end{document}

I don't need a very general solution. Even if it just tested the first character as being a negative sign would suffice for my application. I would think that the etoolbox could do this easily, but I can't quite figure out how. If anyone knows of a good document that contains examples of using etoolbox that would be great.

Peter Grill
  • 223,288

6 Answers6

9
\documentclass{article}

\makeatletter
\newcommand\PrintSignOfNumber[1]{%
  \ifdim#1pt<\z@ $#1<0$\else$#1\ge0$\fi}
\makeatother

\begin{document}

\PrintSignOfNumber{2}\par 
\PrintSignOfNumber{-2}

\PrintSignOfNumber{2.0}\par 
\PrintSignOfNumber{-2.0}

\end{document}
  • +1 This would be the simplest way to go. However one drawback is that it will cause a TeX compile error if the argument is not a number (anything else than +/-, digits, .). This is of course a general problem, but other solutions would be simple wrong, not causing an error. Ok, sometimes an error is what you want. This points you directly to the wrong input. – Martin Scharrer Apr 08 '11 at 13:12
  • it is also simple to check whether I have a valid dimension or not ... And, by the way: my solution also works for 2,1 which will be friendly to us germans :-) –  Apr 08 '11 at 13:34
  • Nice. I didn't know that TeX accepts a , as well. – Martin Scharrer Apr 08 '11 at 13:37
  • 1
    Actually, I don't consider that a drawback. Its much more preferable to get a compile error rather than silently do the wrong thing. – Peter Grill Apr 08 '11 at 17:50
  • Nice solution.. – Gonzalo Medina Apr 08 '11 at 21:02
7

An easy way is to use a simple macro and test if the number is less than zero.

\documentclass{article}
\begin{document}

\def\test#1{%
\expandafter\ifnum#1<0 isNegative \else isPositive\fi
}
\test{-23}

\test{23}
\end{document}

For decimal numbers, modify as:

\documentclass{article}
\usepackage{fp}
\begin{document}

\def\test#1{%
\expandafter\ifnum#1<0 isNegative \else isPositive\fi
}
\test{-23}

\test{23}

\def\testReal#1{%
\FPiflt{#1}{0} isNegative\else isPositive\fi 
}

\testReal{-23.789}
\end{document}
yannisl
  • 117,160
  • For your second solution (the one using fp) you could also use fp-basic's macros \FPifneg#1 ...\else...\fi, \FPifpos#1 ...\else...\fi, and \FPifzero#1...\else...\fi. Also, just a minor remark: zero is neither positive nor negative. – Gonzalo Medina Apr 08 '11 at 23:17
  • @Gonzalo thanks for the comments and yes the code should have a test for zero as well. – yannisl Apr 10 '11 at 21:33
7

You can use \@ifnextchar{<char>}{<true>}{<false>} to check if the next character (actually next token) is -. I would also recommend to not use some \if TT\fi trickery but use a if-macro which accepts the true and false branches as arguments.

Here the argument is unbraced by the first macro and placed behind \@ifnextchar which is normally the last element in a macro to read the next character after it. The rest of the argument is then removed by \remove@to@nnil which removes everything to \@nnil. All of these macros are from the LaTeX kernel.

\documentclass{article}

\makeatletter
\def\ifPositive#1{%
    \@ifnextchar{-}%
      {\expandafter\@secondoftwo\remove@to@nnil}%
      {\expandafter\@firstoftwo\remove@to@nnil}%
        #1\@nnil
}
\makeatother

\newcommand{\PrintSignOfNumber}[1]{%
    \ifPositive{#1}%
        {$#1 \ge 0$}%
        {$#1 < 0$}
}

\begin{document}

\PrintSignOfNumber{2}\par 
\PrintSignOfNumber{-2}

\PrintSignOfNumber{2.0}\par
\PrintSignOfNumber{-2.0}

\end{document}

Prints:

 2 >= 0
-2 < 0
2:0 >= 0
-2:0 < 0

If your really want if-switches then you can do it like follows. Just make sure that the macro definition isn't inside a conditionals, otherwise the \fi, \iftrue and \iffalse will confuse TeX.

\documentclass{article}

\makeatletter
\def\isPositive#1{%
    TT\fi
    \@ifnextchar{-}%
    {\expandafter\iffalse\remove@to@nnil}%
    {\expandafter\iftrue\remove@to@nnil}%
        #1\@nnil
}
\makeatother

\newcommand{\PrintSignOfNumber}[1]{%
    \if\isPositive{#1}%
        $#1 \ge 0$%
    \else
        $#1 < 0$
    \fi
}

\begin{document}

\PrintSignOfNumber{2}\par
\PrintSignOfNumber{-2}

\PrintSignOfNumber{2.0}\par
\PrintSignOfNumber{-2.0}

\end{document}
Martin Scharrer
  • 262,582
7

If you are an adept of TikZ, the next solution is for you (with pgf 2.1)

\documentclass[]{scrartcl}
\usepackage{tikz}
\newcommand\test[1]{
\pgfmathsetmacro{\var}{#1}
\pgfmathparse{ifthenelse(\var>=0,"positif","négatif")} \pgfmathresult}%

\begin{document} 

\test{1}   \test{-2} \test{cos(95)}

\end{document}

Modified version :

\documentclass[]{scrartcl}
\usepackage{tikz}

\newcommand\ifPositif[3]{ 
\begingroup
\pgfmathsetmacro{\var}{#1}
\pgfmathparse{ifthenelse(\var>=0,1,0)} 
\ifdim\pgfmathresult pt= 1 pt 
   #2
  \else 
   #3
\fi 
\endgroup
}

\begin{document}

\ifPositif{1}{pos}{neg} 

\ifPositif{2-2}{\pgfmathparse{sqrt(3)} \pgfmathresult}
                {\pgfmathparse{sqrt(2)} \pgfmathresult} 

\ifPositif{cos(95)+sqrt(2)}{\pgfmathparse{sqrt(5)} \pgfmathresult}
                            {\pgfmathparse{sqrt(6)} \pgfmathresult}

\ifPositif{-1e-2}{pos}{neg}

\ifPositif{1-5e2}{\var}{\foreach \x in {1,2}{\x}} 

\ifPositif{1-5e2}{\var}{\var}   
\end{document}
Alain Matthes
  • 95,075
  • All the solutions here work great. I like this the best as it is a lot more flexible, but I would prefer to have a wrapper macro so when I need to use this I can just say something like \ifPositive{}{do this}{else do this}. I'm going to try to attempt to do this, but I am still only at the hacker stage when it comes to TeX – Peter Grill Apr 08 '11 at 17:53
  • @Peter I add the wrapper macro. – Alain Matthes Apr 08 '11 at 19:08
  • Works great. Not sure I fully understand the comparison to "1 pt". – Peter Grill Apr 08 '11 at 19:27
  • @Peter you can use \if\pgfmathresult 1 \relax instead of \ifdim – Alain Matthes Apr 08 '11 at 19:42
  • Just a minor remark: zero is neither positive nor negative; you could modify your example codes so that 0 is not reported as positive. – Gonzalo Medina Apr 08 '11 at 23:11
  • @Gonzalo In french "zéro" is negative AND positive, but I know in english, zero is neither positive nor negative. I don't know for other country ? – Alain Matthes Apr 09 '11 at 08:06
  • I didn't know that convention in French; in Spain and Latin America zero is neither positive nor negative. – Gonzalo Medina Apr 09 '11 at 13:40
6

Very late to the party, but a general solution does exist.

\documentclass{article}
\usepackage{xfp}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\FPCompareTF}{mmm} { \fp_compare:nTF { #1 } { #2 } { #3 } }

\ExplSyntaxOff

\newcommand{\PrintSignOfNumber}[1]{% \FPCompareTF{#1 >= 0}{$\fpeval{#1}\ge0$}{$\fpeval{#1}<0$}% }

\begin{document}

\PrintSignOfNumber{0}\par \PrintSignOfNumber{2}\par \PrintSignOfNumber{2.0}\par \PrintSignOfNumber{-2.37}\par \PrintSignOfNumber{22/7-pi}\par \PrintSignOfNumber{3+10/71-pi}\par

\edef\pson{\FPCompareTF{3+10/71-pi>0}{positive}{negative}} \texttt{\meaning\pson}

\end{document}

enter image description here

egreg
  • 1,121,712
1

This method works regardless of integer or real type argument

\documentclass{article}
\usepackage{stringstrings}
\newcommand\PrintSignOfNumber[1]{%
  \isnextbyte[q]{-}{#1}%
  \if T\theresult$#1 < 0$\else$#1 \ge 0$\fi%
}
\begin{document}
\PrintSignOfNumber{2}\par 
\PrintSignOfNumber{-2}\par
\PrintSignOfNumber{2.0}\par
\PrintSignOfNumber{-2.0}
\end{document}