8

I'm trying to create a number of running totals using the FP package. I define a macro for adding numbers to a running total and then I define a macro to output the final value.

The problem I'm having is that I receive a different output from the totals macro depending on where I call it. If it's inside the table I receive the correct value however if it's outside then the value doubles.

Any ideas?

\documentclass[11pt]{report}

\usepackage{fp} 
\usepackage{tabu,longtable}

\FPset\totalone{0}
\FPset\totaltwo{0}

\def\add#1{%
  \FPadd\0\totalone{#1}
  \global\let\totalone\0
  \FPadd\0\totaltwo{#1}  
  \global\let\totaltwo\0
  #1
}

\def\ptotalone{%
  \totalone
  \FPset\0{0}\global\let\totalone\0
}

\def\ptotaltwo{%
  \totaltwo
  \FPset\0{0}\global\let\totaltwo\0
}

\begin{document}

  \begin{table}[htbp]
    \begin{longtabu}{c}
      \add{0.25} \\
      \add{0.5}  \\
      \add{1}    \\
      total one: \ptotalone
    \end{longtabu}
  \end{table}
  total two: \ptotaltwo

\end{document}

Example output:

0.25
0.5
1
total one: 1.75
total two: 3.5
David Carlisle
  • 757,742

2 Answers2

9

tabu inherits some code from tabularx to disable \write during its trial runs, so you can just do your arithmetic on the final run when \write has its normal value.

\documentclass[11pt]{report}

\usepackage{fp} 
\usepackage{tabu,longtable}

\FPset\totalone{0}
\FPset\totaltwo{0}
\let\origwrite\write

\def\add#1{%
\relax\ifx\origwrite\write
  \FPadd\0\totalone{#1}
  \global\let\totalone\0
  \FPadd\0\totaltwo{#1}  
  \global\let\totaltwo\0
\fi
  #1
}

\def\ptotalone{%
  \totalone
  \FPset\0{0}\global\let\totalone\0
}

\def\ptotaltwo{%
  \totaltwo
  \FPset\0{0}\global\let\totaltwo\0
}

\begin{document}

  \begin{table}[htbp]
    \begin{longtabu}{c}
     \add{0.25} \\
      \add{0.5}  \\
      \add{1}    \\
      total one: \ptotalone
    \end{longtabu}
  \end{table}
  total two: \ptotaltwo

\end{document}
ShreevatsaR
  • 45,428
  • 10
  • 117
  • 149
David Carlisle
  • 757,742
  • 1
    Nice, but I'd prefer that tabularx (and tabu) had a proper switch like \ifmeasuring@ in amsmath for distinguishing between the two stages, rather than relying on \ifx\origwrite\write. – egreg Oct 18 '13 at 15:13
  • @egreg I could do something about one of those:-) – David Carlisle Oct 18 '13 at 15:19
  • @DavidCarlisle any idea of how to adapt your trick to tabularray package? See here: https://tex.stackexchange.com/q/674379/46718 – Tobard Feb 23 '23 at 11:02
  • 1
    tabularray has added a command for this https://github.com/lvjr/tabularray/issues/179#issuecomment-1399695943 @Tobard – David Carlisle Feb 23 '23 at 11:56
  • @DavidCarlisle This sounds very good! Don't know how to get such a recent package version but I will try this. – Tobard Feb 23 '23 at 20:02
6

The tabu (and so also longtabu) environments do two passes over the material, so your operations are performed twice. Unfortunately there doesn't seem to exist a conditional that's set in only one of the passes in order to do register settings or assignments only once (similar to \ifmeasuring@ of amsmath).

So the answer seems to be: don't use tabu. Note that, as said by the author, the next version of tabu will not guarantee back compatibility, so I can't recommend using it (the author answered me saying, about back compatibility, "I don't care").

egreg
  • 1,121,712