5

There are several typed pattern-matching commands in expl3, such as \tl_case, \str_case etc., including \int_case, but, notably, there doesn't seem to be a corresponding \fp_case for matching floating point values. I am using the current l3kernel documentation for reference.

Am I missing something trivial or is \fp_case actually missing at the moment? If so, I'd appreciate some pointers towards implementing it, as I'm still new at using LaTeX3 internals. TIA.

6
  • 2
    It would be a bit pointless, because you can have rounding errors and then 3.1415926535897932 would not match 3.1415926535897931... – Phelype Oleinik Jun 11 '20 at 16:04
  • 1
    The short answer is going to be 'there isn't one', but I wonder what you are up to where you feel a floating point 'case' switch makes sense – Joseph Wright Jun 11 '20 at 16:04
  • @PhelypeOleinik That's true (also in C and other "conventional" languages), I was just wondering about the existence of \fp_compare and the possibility of expanding on the same idea. I might very well be misunderstanding something here, though. – Arets Paeglis Jun 11 '20 at 16:13
  • @JosephWright Terrible things. – Arets Paeglis Jun 11 '20 at 16:13
  • @AretsPaeglis With \fp_compare:nNnTF, you are often looking for 'more than X' or 'less than X', which doesn't have that issue. Or you are looking for reliable marker values (like exactly zero). – Joseph Wright Jun 11 '20 at 16:26
6

It doesn't exist because it doesn't make much practical sense. Any inaccuracy you get from a specific computation will make your code do a different thing, even if 3.1415926535897932 and 3.1415926535897931 aren't really different. For example, this:

\fp_compare:nNnTF { sin(2pi) } = { 0 }
  { \TRUE } { \FALSE }

yields false, even though analytically it should be true.

Floating-point equality is generally the wrong operation. Most of the time it works because floating-point engines are really good at their jobs, but if you feed them a tricky case it will get the wrong result. In fact, when I used to write Fortran code, gfortran would raise a compile-time warning about floating-point equality (I don't have it installed to give an example, sorry).

And the issue with \fp_case:nn would be that it would use only equality tests to select a case, and that could work, yes, but it could also go wrong, so mainly for stability, it doesn't exist.

That said, we do have \bool_case_true:n(TF) and \bool_case_false:n(TF), which you can use to emulate a \fp_case:nn which checks if a value matches within a set tolerance. Here's a proof-of-concept (no true/false branching implemented):

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\fp_new:N \l_arets_tol_fp
\fp_set:Nn \l_arets_tol_fp { 1e-6 }
\cs_new:Npn \arets_fp_case:nn #1
  {
    \exp_args:Nf \__arets_fp_case:nn
      { \fp_eval:n {#1} }
  }
\cs_new:Npn \__arets_fp_case:nn #1#2
  {
    \tl_map_tokens:nn {#2}
      { \__arets_fp_case_split:nn {#1} }
  }
\cs_new:Npn \__arets_fp_case_split:nn #1#2
  { \__arets_fp_case:nnn {#1} #2 }
\cs_new:Npn \__arets_fp_case:nnn #1#2#3
  {
    \fp_compare:nT
      {
        #1 > #2 - \l_arets_tol_fp &
        #1 < #2 + \l_arets_tol_fp
      }
      { \tl_map_break:n {#3} }
  }

% ------------ Test macro
\cs_new_protected:Npn \test #1
  {
    $
    \arets_fp_case:nn {#1}
      {
        { { pi }   { \pi } }
        { { exp(1) } { e } }
      }
    = \fp_eval:n {#1} $
  }

\ExplSyntaxOff
\begin{document}
\test{3.1415926}

\test{2.7182818}

\test{1.6180339}
\end{document}

which prints:

enter image description here

1
  • 1
    Thank you for the detailed response! I know about the issues with rounding in switch statements in general, I was wondering exactly about this type of "precision-bounded" approach. – Arets Paeglis Jun 11 '20 at 16:57

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.