I am having some difficultly comparing two numbers passed as arguments using \ifnum. Consider the command

\def\comparenum[#1,#2]
{
  \def\x{#1}
  \def\y{#2}
  \ifnum\x=\y
   TRUE
  \else
   FALSE
  \fi
}

\comparenum[1,1] gives TRUE as I would expect. However, if I replace the above with

\ifnum\x=\y
  1
\else
  0
\fi

The same call \comparenum[1,1] produces 0 (replacing \y with \y{}, or indeed #2 seems to fix this). What is the reason for this behaviour? I would like to use the second construction so that I can nest the statement inside another conditional as in the top answer to How to form “if … or … then” conditionals in TeX?

  • 1
    You may want to look into etoolbox and its \ifnumequal test. Together with \ifboolexpr you can easily nest stuff. But you can also look into expl3 and its vast array of tests. – moewe Oct 21 at 12:19
  • Your first code produces TRUE but does not produce FALSE if you call \comparenum[1,2]. – Sigur Oct 21 at 12:19
  • @Sigur I just realised the same. \def\x{#1} \def\y{#1} could explain that ;-) But I'm wondering why the \def's are needed at all... \ifnum#1=#2 should do more or less the same. – moewe Oct 21 at 12:20
  • If you want to be able to use 1 and 0 as "return" values you should try \ifnum\x=\y\relax – moewe Oct 21 at 12:21
  • @moewe, ohhhhh, I missed that!!! Good! – Sigur Oct 21 at 12:21
up vote 8 down vote accepted

Your second code is equivalent to

\def\comparenum[#1,#2]{ \def\x{#1} \def\y{#1} \ifnum\x=\y1 \else0 \fi}

(because spaces and end-of-lines are ignored after a control word). Now you should be able to see the main problem with the code. When TeX evaluates the conditional, it needs two numbers and does full expansion until finding tokens that cannot be interpreted as digits.

So it expands \x and = stops the search for digits while also starting the lookup for the next number; \y is expanded and 1 follows. So the call \comparenum[1,1] translates into

\ifnum1=11 \else0 \fi

which of course returns false.

You solve the issue with

\def\comparenum[#1,#2]{%
  \def\x{#1}%
  \def\y{#1}%
  \ifnum\x=\y\relax
    1% 
  \else
    0%
  \fi
}

The \relax token stops the lookup for digits.

On the other hand, this construct is not expandable. You can make an expandable version with

\def\comparenum[#1,#2]{%
  \ifnum#1=#2\space
    1%
  \else
    0%
  \fi
}

The \space expands to a space token that stops \ifnum from looking up for more digits and is then ignored by rule. However, this would leave a space if used with a counter register. A safer version would be

\def\comparenum[#1,#2]{%
  \ifnum#1=\expandafter\id\expandafter{\number#2}\space
    1%
  \else
    0%
  \fi
}
\def\id#1{#1}

An e-TeX version would be simpler:

\def\comparenum[#1,#2]{%
  \ifnum#1=\numexpr#2\relax
    1%
  \else
    0%
  \fi
}

Please, note the % that protect the end-of-lines avoiding that they make spaces in the output.

  • This both clearly explains and resolves the issue - thank you! Do you know why I can get away with \ifnum\x=#2 1 \else0 \fi but not \ifnum\x=\y 1 \else0 \fi in the expandable version? – Pippip19 Oct 21 at 13:14
  • @Pippip19 In the first case, the space after #2 is gobbled up when it stops the lookup for a number; however it would be show up if you call \comparenum[42,\pageno], because spaces are not ignored when the number is given as an integer register. In the second case there is no space between \y and 1, because spaces are ignored after control words. – egreg Oct 21 at 13:21
  • This makes complete sense - thanks again. I have certainly been getting away with using the conditionals without really understanding the syntax for too long! – Pippip19 Oct 21 at 13:33

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.