5

I want to have a command Unit with 1 parameter that return :

  • nothing if the parameter is 1
  • - if the parameter is -1
  • the parameter in any other case

The goal is to show x and -x instead of 1*x* and -1*x* in my randomly generated exercices.

Here is the command I made :

\newcommand\Unit[1]{\FPifeq#1{1}{}\else\FPifeq#1{-1}{-}\else#1\fi\fi}

However, it generate the following error

Extra \fi

Minimal example :

\documentclass[a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[french]{babel}
\usepackage[T1]{fontenc}
\usepackage{geometry}
\geometry{hmargin=1cm,vmargin=1cm}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{fp}


\begin{document}

\newcommand\Unit[1]{\FPifeq#1{1}{}\else\FPifeq#1{-1}{-}\else#1\fi\fi}

\Unit{2}x \Unit{1}x \Unit{-1}x


\end{document}
  • please put minimal examples inline not as an external link (this question will be archived forever, but that link will most likely break, or the document there will change) – David Carlisle Jul 30 '19 at 7:33
5

The macro \FPifeq doesn't like to be nested in a conditional, because it is not a conditional. Its structure is basically

  • do some comparisons that eventually help to decide whether to execute \FP@testtrue or \FP@testfalse, which make \ifFP@test equal to either \iftrue or \iffalse;
  • set \ifFPtest equal to \ifFP@test;
  • release \ifFPtest in the input stream.

However, TeX does not recognize \FPifeq as a conditional, so with your macro

\FPifeq#1{1}{}\else\FPifeq#1{-1}{-}\else#1\fi\fi

the following happens

  • when #1 is 1, then \ifFPtest is \iftrue, and we end up with

    \iftrue{}\else\FPifeq#1{-1}{-}\else#1\fi\fi
    

    Now {} will be left in the input stream and the \else will remove everything up to the matching \fi. Oh, no! Another \fi remains!

  • when #1 is not 1, then \ifFPtest is \iffalse, and we end up with

    \iffalse{}\else\FPifeq#1{-1}{-}\else#1\fi\fi
    

    Now everything up to \else is removed and we remain with

    \FPifeq#1{-1}{-}\else#1\fi\fi
    

    but TeX remembers it has removed an \else. Then everything will work well, because at the end of the job of \FPifeq either \iftrue or \iffalse will result. The trailing \fi corresponds to the \else removed in advance, so it doesn't harm.

There are a few glitches in your code: it should be {#1} throughout, otherwise a value such as 12 would take 1 as the first argument to \FPifeq and 2 as the second argument.

You could fix the nesting problem by doing

\newcommand{\FPeq}{TT\fi\FPifeq}
\newcommand\Unit[1]{%
  \if\FPeq{#1}{1}%
    % do nothing
  \else
    \if\FPeq{#1}{-1}%
      -%
    \else
      #1%
    \fi
  \fi
}

This is an old trick that uses \if to make the problem above vanish; if \if is expanded, it sees TT\fi, which results in a true test and \FPifeq will be executed; if it is in skipped text, it will correctly match a corresponding \else or \fi.

However, the usage of \FPifeq has bad consequences on the final output. Let's see an example:

\documentclass{article}
\usepackage[nomessages]{fp}

\newcommand{\FPeq}{TT\fi\FPifeq}
\newcommand\Unit[1]{%
  \if\FPeq{#1}{1}%
    % do nothing
  \else
    \if\FPeq{#1}{-1}%
      -%
    \else
      #1%
    \fi
  \fi
}

\begin{document}

$\Unit{2}x$ $\Unit{1}x$ $\Unit{-1}x$

$2x$ $x$ $-x$

\end{document}

enter image description here

Can you see the problem? The working of \FPifeq creates an empty math atom, so the minus sign is treated as a binary operator rather than a unary, if leading the polynomial.

The problem does not show with Phelype Oleinik's code using expl3.

  • Thanks for this deep explaination. Also thanks about using {#1} instead of #1. I made this error multiple times in my document and I would never have understand why my code suddently stopped working the day I would trigger the error. – Matheod Jul 30 '19 at 12:36
8

You don't need fp for that. TeX can do integer comparison with \ifnum. This:

\documentclass{article}
\newcommand\Unit[1]{%
  \ifnum#1=1
    % nothing
  \else
    \ifnum#1=-1
      -%
    \else
      #1%
    \fi
  \fi}
\begin{document}
\Unit{2}x \Unit{1}x \Unit{-1}x
\end{document}

produces 2x x -x.


If you want to use fp, then you need to hide the second test in another macro. Apparently fp doesn't like nested conditionals:

\newcommand\Unit[1]{%
  \FPifeq{#1}{1}%
    % nothing
  \else
    \UnitInner{#1}%
  \fi}
\newcommand\UnitInner[1]{%
  \FPifeq{#1}{-1}%
    -%
  \else
    #1%
  \fi}

However, I recommend you use expl3 and it's own FPU. It's much more flexible and allows you to easily compose nested conditionals:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand \Unit { m }
  {
    \fp_compare:nF { #1 == 1 }
      {
        \fp_compare:nTF { #1 == -1 }
          { - }
          { #1 }
      }
  }
\ExplSyntaxOff
\begin{document}
\Unit{2}x \Unit{1}x \Unit{-1}x
\end{document}
  • Ty. By curiosity, do you know why I got this error and how to fix it ? I might need to do non integer comparison one day. – Matheod Jul 30 '19 at 4:31
  • @Matheod I complemented the answer. – Phelype Oleinik Jul 30 '19 at 5:04
  • Thanks for the clarification – Matheod Jul 30 '19 at 5:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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