Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
2  
Not an answer to the matter in hand, but you know that siunitx can do something very similar using the scientific-notation and fixed-exponent settings, I guess. –  Joseph Wright Feb 26 '11 at 19:39
    
(Comment posted as the example uses siunitx!) –  Joseph Wright Feb 26 '11 at 19:39
    
@Joseph I am using siunitx in other parts of the code. These macros though return the numbers in scientific notation automatically from calculations, i.e., you do not need to write num{1.234e10}, it takes the number and rather formats automatically. Also sometimes siunitx does not play well with the fp package. –  Yiannis Lazarides Feb 26 '11 at 19:50
    
My point about the scientific-notation option is that you can use it to convert 1234000000 into something which is displayed as 1.234e10 by siunitx. Do tell more about the last point: bug reports are always welcome! –  Joseph Wright Feb 26 '11 at 19:53
add comment

3 Answers 3

up vote 8 down vote accepted

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!)

share|improve this answer
add comment

Since fp stores its numbers in decimal, I'd simply count the number of digits, or barring that, use a logarithm to compute the exponent.

As a more general rule, loops in TeX are very easy to use.

\loop
    % stuff that always happens
    \if... % the loop condition
    % stuff that happens if the condition is true
\repeat

I tend to write loops that look like

\loop\if... % whatever the condition is
    % loop body
\repeat

Here's a complete example.

\catcode`@=11
\count@=0
\loop\ifnum\count@<256
    \char\count@\
    \advance\count@ by1
\repeat
\bye
share|improve this answer
add comment

If you don't want to use loops (which do work well, as mentionned above, although the plain TeX version should not be nested), you can avoid repeated code by defining a macro to do the job. For example, one piece of your code could be rewritten as

\def\myFPifgt #1#2{
    \FPifgt\Number#1
        \FPdiv\temp{\Number}{#1}
        \def\Epower{#2}
        \global\let\result\temp
    \fi
}

\myFPifgt {\PowerThree} {3}
\myFPifgt {\PowerFour} {4}
\myFPifgt {\PowerFive} {5}
\myFPifgt {\PowerSix} {6}

%etc.
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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