[Tex/LaTex] Support for “tables of functions”

calculationstables

Prior to that arrival of calculators, we were cursed (or blessed) with "log tables", tables of functions to a fixed number of significant figures

example

Is there any (La)TeX support for such tables? Or other software which outputs such in (La)TeX format?

Best Answer

Edit: I am adding a new method which is very nice for computing one by one digits of base 10 logarithms. It was inspired directly from this nice paper.

It is quite faster (I mean the implementation here at TeX macro level) than the method implemented next based on Borchardt algorithms operations.

(the background is that the poor math engine I am using has been too lazy to implement log so far so we have to find workarounds)

\documentclass[letterpaper]{article}
\usepackage{geometry}
\usepackage{xintexpr}

% Computation of logarithms via a simple-minded digit by digit algorithm
% reference
% https://tidsskrift.dk/brics/issue/view/3152
% We work with a sequence of floating point numbers x_n,  1 <= x_n < 10

% algorithm for a new digit :

%  x_n**10 = x_{n+1} times 10**d_{n+1}

% This means \xintFloatPow{x_n}{10} expands to x_{n+1} e d_{n+1}

% Strangely xint is lacking a macro to get exponent of a floating point
% number ? we do it by hand

\makeatletter
\def\GetOneMoreDigit {%
    \expandafter\GetOneMoreDigit@\romannumeral0\xintfloatpow{\X}{10}!%
}%

\def\GetOneMoreDigit@ #1e#2!{\def\X{#1e0}\def\D{#2}}

\def\GetAndPrintFourRoundedDigits #1{\def\X{#1}%
    \GetOneMoreDigit\let\Da\D
    \GetOneMoreDigit\let\Db\D
    \GetOneMoreDigit\let\Dc\D
    \GetOneMoreDigit\let\Dd\D
    \GetOneMoreDigit\let\De\D
    \expandafter\@gobble\the\numexpr
       1\Da\Db\Dc\Dd+\ifnum\De>4 1\else 0\fi\relax
}%
\makeatother


\usepackage{array}

\begin{document}

\begin{table}[htbp]
\centering
\caption{Table of logarithms}
% use \xintDigits:=8; ?? does not seem to increase speed a lot
 \begin{tabular}{|r||*{5}{c}|*{5}{c}|}
  \hline
  N\xintFor* #1 in {0123456789}\do
    {&\multicolumn{1}{c|}{#1}}\\\hline
%
\xintFor* #1 in {\xintSeq{10}{24}}\do {%
    #1\xintFor* #2 in {0123456789}\do {%
         & \GetAndPrintFourRoundedDigits{#1#2e-2}
         }% fin de boucle avec #2
  \ifnum#1<24 \ifnum\numexpr#1+1-((#1+1)/5)*5=0 \\[1ex]\else\\\fi\else\\\fi
  }% fin de boucle avec #1
  \hline
\end{tabular}
\end{table}

\end{document}

enter image description here


Because xintexpr is still lacking log I, for fun, did a (high level) usage of Borchardt's algorithm.

A tad slow, but well not optimized in any way... (except cutting the table to not too many rows ;-)).

I took the canvas from this answer

\documentclass[letterpaper]{article}
\usepackage{geometry}
\usepackage{xintexpr}

% Computation of logarithms via Borchardt's algorithm
% Just for fun, because sqrt is available, so let's try this out

\xintdeffloatfunc B(a, b):= subs((c, sqrt(c*b)), c = (a+b)/2);

% Currently, one must go via a macro-like definition when abstracting
% usage of "iter". This means the whole parsing is done again
% at time of execution. Perhaps in future, one could use here
% \xintdeffloatfunc

% \BDigits is a parameter to be set later. This is like a macro
% definition, it does no parsing nor expansion.
\xintNewFunction{log}[1]{%
    iter((1+#1)/2, sqrt(#1);           % initial values
         (abs([@][0]-[@][1]) < 1[-\BDigits])? % stop iterating ...
           {break(2*(#1-1)/([@][0]+[@][1]))}  % ... and do final computation,
           {B(@)}, % else iterate via "B" formulas
         i=1++) % The i is not used. Only serves to generate iteration
}

% Compute log(10) with circa 8 or 9 digits of precision
\xintDigits:=10;
\def\BDigits{8}
\xintverbosetrue
\xintdeffloatvar LnTen:=log(10);

% (we will need less precision for the table itself)

\usepackage{array}

\begin{document}

\begin{table}[htbp]
\centering
\caption{Table of logarithms}
\xintDigits:=8;
\def\BDigits{5}% Precision to be achieved in Borchardts algorithm
% (do not take it too close to \xintDigits value)
\begin{tabular}{|>{\bfseries}c|*{10}{r|}}
  \hline
  N\xintFor* #1 in {0123456789}\do
    {&\multicolumn{1}{c|}{#1}}%
   \\\hline
\xintFor* #1 in {12}\do {%
  \xintFor* #2 in {0123456789}\do {%
    #1.#2\xintFor* #3 in {0123456789}\do {%
         &\xinttheiexpr [4]
              \xintfloatexpr log(#1.#2#3)/LnTen\relax
          \relax
         }% fin de boucle avec #3
    \\\hline
    }% fin de boucle avec #2
  }% fin de boucle avec #1
% add last row
% 4.0\xintFor* #3 in {0123456789}\do {%
%          &%\np{% in case \np macro of numprint is used
%            \xinttheiexpr
%               10000*\xintfloatexpr log(4.0#3)/LnTen\relax
%            \relax
%            %}%
%          }% fin de boucle avec #3
%\\\hline
\end{tabular}
\end{table}

\end{document}

enter image description here


Variant algorithm for 48 digits logarithms!

We use Newton method, assuming we have an exp function. But we don't have an exp function so we must program it too...

A bit slow in the end...

\documentclass[]{article}
\usepackage{geometry}
\usepackage{xintexpr}

%\newcommand{\FPprecision}{48}% we will need to set \xintDigits to some higher
                             % value, say 52 for 4 guard digits
% anyway I will hardcode this for the moment


% again, as we use "iter" statement, there is currently
% no way to convert this into expandable macro calls
% having done already all parsing. So we use simply
% "macro encapsulations"

% We need e=exp(1) computed already,
% find first N! > 1e54

\xintverbosetrue % push to logsvariable definitions

% attention that the first ; must be hidden from \xintdefiivar :-((
% and use num(1e54) to convert to explicit digits as "ii" parser
% is for strict integers
\xintdefiivar Nmin := iter(2{;}(@>num(1e54))?{break(i-1)}{i*@}, i=3++);

% turns out to be 44

% compute the corresponding value of e. As this uses
% the float parser, value of \xintDigits must now be set (else uses 16 per default)
\xintDigits:=52;
% problem is that each addition will be done with 52 digits
% precision only. But 52 is big enough compared to 48 digits
% which is our final goal.

% attention again to first semi-colon
\xintdeffloatvar e:=`+`(rseq(1{;} @/i, i= 1..Nmin));

% If the latter use this 'e' with a lower \xintDigits,
% it will be rounded *before* actual operations, but
% we stick here with our \xintDigits set to 52

% of course we could organize that easier if we dropped expandability!

\xintNewFunction{expt}[1]{% the #1 will actually be negative > -1 in our usage
    iter(1, #1; (abs([@][1]) < 1e-48) ?     % check if we abort
               {break([@][0]+[@][1])}       % yes, precision reached (add the last one nevertheless)
               {([@][0]+[@][1], [@][1]*#1/i)}% iterate
               ,i = 2++)% first iteration computes 1+x and x^2/2
}%
% x = num(x) + frac(x), num is truncation of x to integer (towards zero), so
% tfrac same sign as x)
\xintNewFunction{exp}[1]{e^num(#1)*expt(frac(#1))}

% Now compute (natural) logarithm by Newton's method

% y_{n+1} = y_n - (1 - x\cdot e^{-y_n}), y_0 = x - 1

\xintNewFunction{log}[1]{%
   iter(#1-1;
   % must use single letter (here "d" stand for "delta") for substitution variable!
   % and the reason for the substitution is to avoid computing multiple times
        subs((d<1e-48)?
              {break(@-d)}
              {@-d}, 
              d=1-#1*exp(-@))
       ,i=1++)% dummy iteration index, not used but needed by iter()
}

\begin{document}

\begin{table}[htbp]
\centering
\caption{Table of high-precision natural logarithms}
\begin{tabular}{|c|r|}
  \hline
  $x$&$\log(x)$\\\hline
\xintFor* #1 in {123456789{10}}\do {%
  #1 &\xinttheiexpr [48]
        \xintfloatexpr log(#1)\relax
      \relax
      \\\hline
}%
\end{tabular}
\end{table}

\end{document}

enter image description here