3

I cannot figure out how to test if an argument is a floating point or integer using expl3.

Take this simple example where I convert \milk{number} into \dotfill, the number, and gallon or gallons with or without the s appropriately, or a special case for 0, empty. The last line, for Albert, causes an error with the classic cryptic error message:

! Use of \??? doesn't match its definition.
<argument> \???  
      ! LaTeX Error: Unknown fp word none.
l.28 Albert has \milk{none}

By testing if #1 is in fact a floating point, I think I would be able to handle this error, but \tl_is_fp or even \tl_is_int don't seem to exist. How can I test if any arbitrary argument is a floating point or integer?

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\milk}{m}{%
    \fp_compare:nTF {#1 = 0}{%if 0 replace with empty
        \dotfill ~ empty%
    }{\fp_compare:nTF {#1 <= 1}{% s or no s
        \dotfill ~ #1~gallon%
        }{\dotfill ~ #1~gallons%
        }
    }
}

\ExplSyntaxOff

\begin{document}

Ron has \milk{0}

Tom has \milk{1/2}

Timy has \milk{1}

John has \milk{2}

Albert has \milk{-}

\end{document}
15
  • I don't think you want to test for an int (a different type0 but test if the floating point is an integer (so floor(x)=x) Commented May 6 at 22:57
  • 1
    do you want to test if the argument is an int or if it is an expression which evaluates to an integer? an int is, basically, a tex count. so to test if something is an int is to test whether it is something created (at the tex level) by \newcount (which is what \int_new:N does with some extra checks etc.). so 2 is not an int. don't you really want to know whether the argument is equal to an integer? Commented May 7 at 5:06
  • 1
    @mico while I was hoping to avoid needing to do that or to use any other packages, I might expand my search. Thank you for the suggestions. Commented May 9 at 18:10
  • 1
    yes, there are no real differences in kind, as I understand it, between the different types of expl3 variable. but there is a difference between an int, a dim and a tl, for example. if you try latexdef \l_tmpa_int, you can see it is \count25 i.e. it is not a macro but a register. whereas latexdef \l_tmpa_tl shows it is a macro. I don't know if this counts as a difference between data types in the sense of other programming languages, but these are different at the tex level. \def\tempa{0} creates a macro - not a count. Commented May 9 at 19:44
  • 1
    to determine whether something can be parsed as an integer, you can examine it character by character to ensure each token is a number. but to determine whether something can be parsed as an fp is harder because not only might you have, say abs(9*3-100), but you might also have user-defined functions. so distinguishing whether it can be evaluated as an fp without trying to evaluate it as an fp looks tricky to me? Commented May 9 at 19:48

1 Answer 1

6

(Added a paragraph about what to do with a negative-number input.)

Here's a LuaLaTeX-based solution. It uses Lua's tonumber function.

It is assumed that if the argument of \milk is a number (or evaluates to a number; e.g., math.abs(-1-1) evaluates to 2), this number is non-negative. I didn't add an explicit test for negative-number input as the OP's code fragment doesn't seem to consider this possibility either. Providing such a test would consist of adding an if s<0 then return "no milk." else ... end structure, starting on a new line right after the s = tonumber ( s ) line.

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}
\directlua{
  function milk ( s )
    if tonumber ( s ) then % -- s is a number or evaluates to a number
      s = tonumber ( s )
      if s == math.floor ( s ) then % -- s is an integer
        s = math.floor ( s )
        if s == 0 then
          return "an empty bucket."
        elseif s == 1 then 
          return "1 gallon of milk."
        else 
          return s .. " gallons of milk."
        end
      else
        return s .. " gallons of milk."
      end
    else % -- s is not a number
      return "no milk."
    end
  end
}
\newcommand{\milk}[1]{\directlua{tex.sprint(milk(#1))}}

\begin{document}
\obeylines % just for this example
Ron    has \milk{"0"}
Sandra has \milk{4/5}
Timmy  has \milk{1}
Ursula has \milk{math.abs(-1-1)}
Victor has \milk{no_milk_today} % with apologies to Herman's Hermits 

Wanda  has \milk{"."}
\end{document}
1
  • 1
    When I saw the OP question, I immediately thought, "Probably better with LuaLaTeX" and hoped that someone would provide that solution. Shazam! Commented May 9 at 21:41

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.