[Tex/LaTex] Refactoring TeX/LaTeX code

programming

Below is some shortened version of some code I wrote that can automatically format a number from decimal to scientific notation. I have been using it for some time now and have a few ideas how to combine it with some other work I have done into a small package for engineering calculations. However, looking back at what was a couple of hours of work, it definitely needs a good overhaul.

The code provides two major author commands \snotation{} and \enotation{} that return the input number in scientific or engineering notation.

I tend to go for readability of code rather than algorithmic elegance and find it easier to write the code than read through it later and change it. Neither loops nor if constructions are very reliable with TeX and I chose to "cut and paste code" rather than using these more error prone parts of TeX constructions.

Where can I find some information about re-factoring and polishing TeX code. Have you got a good strategy or tips and suggestions?

Here is the code as a working minimal (excludes the engineering part).

\documentclass{article}
\usepackage{fp,siunitx}
\begin{document}

\gdef\SetNumDecimals#1{\gdef\numdecimals{#1}}
\SetNumDecimals{10}
%% Helper routine for formatting numbers
%% first it round to 4 decimal numbers
%% then it clips the number so that if trailing
%% zeroes exist it removes them prints 16.01 and not 16.010000000
\newcommand\Clip[1]{\FPround\tmp{#1}{\numdecimals}\FPclip\tmp{\tmp}\tmp}%
\newcommand\Clipsmall[1]{\FPclip\tmp{#1}\tmp}%

\gdef\typeset#1#2{
  \def\clippednumber{\Clip{#1}}
  \def\powers##1{$##1 \times10^{#2}$}
  \powers{\clippednumber}
 }

\gdef\typesetsmall#1#2{
  \def\clippednumber{\Clipsmall{#1}}
  \def\powers##1{$##1 \times10^{#2}$}
  \powers{\clippednumber}
 }

\FPset\PowerOne{10}
\FPset\PowerTwo{100}
\FPset\PowerThree{1000}
\FPset\PowerFour{10000}
\FPset\PowerFive{100000}
\FPset\PowerSix{1000000}
\FPset\PowerSeven{10000000}
\FPset\PowerEight{100000000}
\FPset\PowerNine{1000000000}
\FPset\PowerTen{10000000000}
\FPset\PowerEleven{100000000000}
\FPset\PowerTwelve{1000000000000}
\FPset\PowerFifteen{1000000000000000}

\gdef\Epower{}
\gdef\result{}

\def\Powers#1{%
\FPset\Number{#1}
\FPifgt\Number\PowerTwo
   \FPdiv\temp{\Number}{\PowerTwo}
   \def\Epower{3}
   \FPset\result\temp
\fi
\FPifgt\Number\PowerThree
   \FPdiv\temp{\Number}{\PowerThree}
   \def\Epower{3}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerFour
   \FPdiv\temp{\Number}{\PowerFour}
   \def\Epower{4}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerFive
   \FPdiv\temp{\Number}{\PowerFive}
   \def\Epower{5}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerSix
   \FPdiv\temp{\Number}{\PowerSix}
   \def\Epower{6}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerSeven
   \FPdiv\temp{\Number}{\PowerSeven}
   \def\Epower{7}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerEight
   \FPdiv\temp{\Number}{\PowerEight}
   \def\Epower{8}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerNine
   \FPdiv\temp{\Number}{\PowerNine}
   \def\Epower{9}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerTen
   \FPdiv\temp{\Number}{\PowerTen}
   \def\Epower{10}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerEleven
   \FPdiv\temp{\Number}{\PowerEleven}
   \def\Epower{11}
   \global\let\result\temp
\fi

\FPifgt\Number\PowerTwelve
   \FPdiv\temp{\Number}{\PowerTwelve}
   \def\Epower{12}
   \global\let\result\temp
\fi
  \typeset{\result}{\Epower}
}

\FPset\one{1}

\def\piconums#1{%
\FPset\Number{#1}
\FPiflt\Number\one
   \FPmul\temp{\Number}{\PowerOne}
   \def\Epower{-1}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerTwo}
   \def\Epower{-2}
   \global\let\result\temp
 \else
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerThree}
   \def\Epower{-3}
   \global\let\result\temp
 \else
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerFour}
   \def\Epower{-4}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerFive}
   \def\Epower{-5}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerSix}
   \def\Epower{-6}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerSeven}
   \def\Epower{-7}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerEight}
   \def\Epower{-8}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerNine}
   \def\Epower{-9}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerTen}
   \def\Epower{-10}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerEleven}
   \def\Epower{-11}
   \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerTwelve}
  \def\Epower{-12}
  \global\let\result\temp
\fi
\FPiflt\result\one
  \FPmul\temp{\Number}{\PowerFifteen}
  \def\Epower{-15}
  \global\let\result\temp
\fi

\typesetsmall{\result}{\Epower}
}

%% main routine
\def\snotation#1{
\FPset\Number{#1}
\FPifgt\Number\one
    \Powers{#1}
   \else
    \piconums{#1}
\fi}

\snotation{0.000000100326}

\snotation{1133009.1495978707}
\end{document}

Apologies for the length of the code.

Best Answer

I'm not sure there is a good guide to refactoring TeX code, but a few things I note from the above you might want to think about:

  • Making global assignments should always make you think 'is this really needed?'. While making things global is quite often easy, other approaches are sometimes better, for example

      \def\TempResult{Result}%
      \expandafter\endgroup
    \expandafter\def\expandafter\RealResult\expandafter{\TempResult}%
    
  • Watch how you deal with \fi. Remember that unless you expand the end of the conditional then they continue to stack up, and can lead to runaway conditions or exhaustion of the stack. This is why approaches such as

      \expandafter\NextFunction
    \fi
    

    or

    \if<something>
      \expandafter\@firstoftwo
    \else
      \expandafter\@secondoftwo
    \fi
      {True code}
      {False code}%
    

    are very common (often the second is pre-packed, for example in etoolbox.

  • Unless you want maximum performance, I'd favour using more, well-named macros over fewer longer ones, and favour macros over primitives (so \@namedef rather than \expandafter\csname ...\endcsname, for example). (If you are really after maximum performance, then even additional \expandafters make a difference, but unless you are writing low-level kernel functions this should not be an issue!)

Related Question