# Support for "tables of functions"

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

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

• The question is a bit unclear. LaTeX has tables, so you can make a table as in the example figure - but that is probably not what you mean? There are also various methods to use loops, mathematics, rounding etc., so you could generate such tables more or less automatically. You can also compute the values and the LaTeX formatting strings in any other programming language, and copy the result in your LaTeX file. I'm not aware of any packages that provide a write log tables function. Apr 21, 2018 at 9:50
• @Marijn : of course, a python script and a standard table environment would do this, I'm just hoping to avoid re-inventing the wheel; and web-searches are hindered by the amount of people who make tables out of (wood) logs :-) Apr 21, 2018 at 10:58

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}


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
% 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}


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}


something like this but you may need to check the rounding logic (I note I get 1731 in the last column, you show 1732) but this will get you started (requires lualatex)

\documentclass{article}

\begin{document}
{\parindent0pt
\catcode\%=12
\directlua{

for i=10,99 do
if (i%5==0) then
tex.sprint("\string\\bigskip")
end
tex.sprint(string.format("%02d: \string\\ ",i))
for j =0,9 do
tex.sprint(string.format("%04d ",10000*math.log(0.1*i+0.01*j,10)))
end
tex.sprint("\string\\par")
end

}}
\end{document}

• I highlighted and indented your code. Please roll back if you don't like it. Apr 22, 2018 at 10:04
• One can deal with the rounding issue by defining a Lua function such as function round2int(x); return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5); end in the preamble and encasing the result of 10000*math.log(0.1*i+0.01*j,10) in a round2int wrapper.
– Mico
Aug 24, 2018 at 15:08

You can do it with expl3, of course.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\logtable}{mm}
{% #1 = start, % #2 = end ( #2 - #1 + 1 should be a multiple of 5)
% the tl will contain the table body
\tl_clear:N \l__jjgreen_logtable_tl
% cycle from #1 to #2
\int_step_inline:nnnn { #1 } { 1 } { #2 }
{
% first add the tens as first column
\tl_put_right:Nn \l__jjgreen_logtable_tl { ##1 }
% compute the common logarithm of 10*#1+(0..9)
\int_step_inline:nnnn { 0 } { 1 } { 9 }
{
\tl_put_right:Nx \l__jjgreen_logtable_tl
{
& \jjgreen_mantissa:n { \fp_eval:n { round(ln(##1*10+####1)/ln(10),4) } }
}
}
\bool_lazy_and:nnTF
{% we're at a fifth row
\int_compare_p:n { \int_mod:nn { ##1 - #1 }{5} = 4 }
}
{% but not at the last
\int_compare_p:n { ##1 != #2 }
}
{ \tl_put_right:Nn \l__jjgreen_logtable_tl { \\[1ex] } }
{ \tl_put_right:Nn \l__jjgreen_logtable_tl { \\ } }
}
\begin{tabular}{ | r || *{5}{c} | *{5}{c} | }
\hline
N & 0 & 1 & 2 & 3 & 4 & 5 & 6 &7 & 8 & 9 \\
\hline
\tl_use:N \l__jjgreen_logtable_tl
\hline
\end{tabular}
}

\tl_new:N \l__jjgreen_logtable_tl
\tl_new:N \l__jjgreen_mantissa_tl
\seq_new:N \l__jjgreen_mantissa_seq

\cs_new_protected:Nn \jjgreen_mantissa:n
{
\seq_set_split:Nnn \l__jjgreen_mantissa_seq { . } { #1 }
\tl_set:Nx \l__jjgreen_mantissa_tl
{ \seq_item:Nn \l__jjgreen_mantissa_seq { 2 } }
\tl_use:N \l__jjgreen_mantissa_tl
\prg_replicate:nn { 4 - \tl_count:N \l__jjgreen_mantissa_tl } { 0 }
}
\ExplSyntaxOff

\begin{document}

\begin{center}
\logtable{10}{24}
\end{center}

\end{document}


The common logarithm of x is computed as ln(x)/ln(10) because expl3 hasn't yet a decimal logarithm function. The result is rounded at the fourth decimal digit and is passed (expanded) to a nonexpanded function for showing just the mantissa. It splits at the decimal period and uses the decimal part, padding with the necessary amount of zeros.

In case you want to use Python in place of Lua, here is the translation of the David Carlisle's answer.

\documentclass[varwidth,border=7mm]{standalone}
\usepackage{python}

\begin{document}

\begin{python}
from math import log

for i in range(10, 99):
if i % 5 == 0:
print('\\bigskip')
print('%02d: \\ ' % i)
for j in range(0, 10):
print('%04d ' % round(10000 * log(0.1 * i + 0.01 * j, 10)))
print('\\par')
\end{python}%

\end{document}
`

• @jfbu Thanks! As usual you are right. The 9th column was missing and the results were truncated and not rounded. I corrected the code and hope that now it is ok.
– Kpym
May 6, 2018 at 21:23
• (ok, comment deleted)
– user4686
May 7, 2018 at 6:29