9

I calc values of the Standard normal table (with 5-digit accuracy).

So I wrote a calculation with numerica.sty (using approximate values ​​of the error function).

This works so far;

enter image description here

if I put in a concrete value like ... [..., x=0.61].

But I would like to have this as a macro, say \sn{<#1>}, for example \sn{0.61}0.72907.

I tried to put the whole thing into a newcommand, but this seems to be not that easy.

I'd like to implement this inside pdflatex, using a package (not Lua, Mathematica, etc.). I've implemented it here using the numerica package because it worked without problems (including the use of the help-variable h, see MWE). However, I'm not sure if numerica is the best choice. The xintexpr package seems promising, but I'm currently unclear on its syntax.

In any case: if anyone knows of a better implementation using a different package (e.g., xint.sty), it would be nice too.

enter image description here

\documentclass[margin=10mm, varwidth]{standalone}
\usepackage{numerica}
\begin{document}
\section{Test with \texttt{numerica}}
\xdef\x{0.61}
sn(\x) = \eval*{%
0.5 (2-h \exp(
-0.5 x^2 -1.26551223 
+1.00002368 h +0.37409196 h^2
+0.09678418 h^3 -0.18628806 h^4
+0.27886807 h^5 -1.13520398 h^6
+1.48851587* h^7 -0.82215223 h^8 
+0.17087277 h^9))
}[h=1/(1+0.25*\sqrt{2}*x),  x=0.61% cannot use \x here :(
][5]

\section{Test with \texttt{xint}}
... ? ....
\end{document}
5
  • 2
    Seriously: Why? I'm sure someone will show up and help you (or I'll do it, once I find the time), but are you sure this is the right approach? I'd just calculate the values in Lua (if using LuaLaTeX) or Python/Matlab/... and write them into a file that I can import into my document. Commented yesterday
  • 1 ("correct values?"). I don't understand the question. Just test the MWE; all the x-values ​​I tested, produced the correct result. 2 ("another program"). Well, we're in the LaTeX forum here. And we can just do this here without having to discuss it. If I want to do the same thing with Python, etc., I can go to the Python forum for that. Commented yesterday
  • PS: I'm not doing / researching this because I have to it or because I'm being paid for it; I'm doing it for enjoyment of the matter. Similarly, I'm allowed to fly to Australia in a zeppelin. Of course, there are other ways to get there. But this doesn't need to be discussed every time... Commented yesterday
  • 3
    I wasn't trying to say "don't do this", I was merely pointing at the fact that other programs or programming languages are more suited for the task. Commented yesterday
  • The dilemma between LaTeX vs Lua and another languages (Python, R, etc.) is somewhat outdated these days, since they can be combined easily with LaTeX in a single file using a package, or a internal conversion, rendering all at once, without the hassle of manually running external programs and then copying and pasting the result into the LaTeX text. So while pure questions about other languages are off-topic, answers or solutions using them as tools into a LaTeX document are on topic, IMHO (of course, unless the OP want to exclude them, for whatever reason). Commented yesterday

3 Answers 3

8

Doing it with xintexpr. Imitating the wikipedia table on this German language page (link from the OP).

\documentclass[margin=5mm, varwidth]{standalone}
\usepackage{xintexpr}
\usepackage[ngerman]{babel}
\usepackage[autolanguage,np]{numprint}
\begin{document}
\section*{Standardnormalverteilungstabelle mit \texttt{xint}}

% https://de.wikipedia.org/wiki/Standardnormalverteilungstabelle#Fl%C3%A4cheninhalte_unter_dem_Graphen_der_Standardnormalverteilung
\[\Phi_{0,1}(z) = \frac1{\sqrt{2\pi}}\int_{-\infty}^z e^{-\frac12t^2}\mathrm{d}t\]

% sn = "standard normal"
% sn(x) = 0.5 + 0.5 erf(x/sqrt(2)))

% approximation to erf (error less than 1.2e-7):
% https://de.wikipedia.org/wiki/Fehlerfunktion#Numerische_Berechnung
% erf(x) \approx  1 - tau(x) for x>=0
% sn(x)  \approx  1 - 0.5 tau(x/sqrt(2))
%
% tau (x) = t * 
% exp (-x^2-1.26551223
%          +1.00002368 t
%          +0.37409196 t^2
%          +0.09678418 t^3 
%          -0.18628806 t^4
%          +0.27886807 t^5
%          -1.13520398 t^6
%          +1.48851587 t^7
%          -0.82215223 t^8
%          +0.17087277 t^9)
% t = 1/(1+0.5 |x|)

% t is renamed to h when used with x/sqrt(2)=0.5 sqrt(2) x

% It turns out this syntax :
%
% \xintNewFloatExpr{\sn}[1]{subsn(1 - 0.5 * h * exp(
%   -0.5*x^2 -1.26551223 
%   +1.00002368*h    +0.37409196*h^2
%   +0.09678418*h^3 -0.18628806*h^4
%   +0.27886807*h^5 -1.13520398*h^6
%   +1.48851587*h^7 -0.82215223*h^8 
%   +0.17087277*h^9), h= 1/(1+0.25*sqrt(2)*x); x = #1)}
%
% works but the computation of h is redone on each occurrence
% in the polynomial...

% This is more efficient
\xintdeffloatfunc snapprox(x,h) = 1 - 0.5 * h * exp(
  -0.5*x^2 -1.26551223 
  +1.00002368*h    +0.37409196*h^2
  +0.09678418*h^3 -0.18628806*h^4
  +0.27886807*h^5 -1.13520398*h^6
  +1.48851587*h^7 -0.82215223*h^8 
  +0.17087277*h^9);

\xintNewFloatExpr{\sn}[1]{snapprox(#1, 1/(1+0.25*sqrt(2)*#1))}

% rounds (by default) to 5 fixed decimal places for final display,
% use package numprint for language specific formatting
% (replace with siunitx if preferred)
\NewDocumentCommand{\SN}{O{5} m}{\np{\xintRound{#1}{\sn{#2}}}}

% alternatively \xintieval[#1]{\foo{#2}} works too.

% Direct input \textbackslash sn\{0.61\} works:
% sn(0.61) = \SN{0.61}

% \def\x{0.61}
% Direct input \textbackslash sn\{\textbackslash x\} - works as well:
% sn(\x) = \SN{\x}

% one can automatize more... (would be needed for easily doing
% variants with other evaluation points)
\newcommand\myrow[1]{\np{#1}&\SN{#10}
                            &\SN{#11}
                            &\SN{#12}
                            &\SN{#13}
                            &\SN{#14}
                            &\SN{#15}
                            &\SN{#16}
                            &\SN{#17}
                            &\SN{#18}
                            &\SN{#19}}

\footnotesize
\noindent
\begingroup
%\xintSetDigits{8} would speed up things a bit by telling
%                  xint to compute only with 8 digits
\setlength{\tabcolsep}{2pt}% normal value is 6pt
% input mark-up should be automatized but well now it is done
\begin{tabular}{r|*{10}{l|}}
\multicolumn{1}{c|}{z}&
\multicolumn{1}{c|}{\np{0}}&
\multicolumn{1}{c|}{\np{0.01}}&
\multicolumn{1}{c|}{\np{0.02}}&
\multicolumn{1}{c|}{\np{0.03}}&
\multicolumn{1}{c|}{\np{0.04}}&
\multicolumn{1}{c|}{\np{0.05}}&
\multicolumn{1}{c|}{\np{0.06}}&
\multicolumn{1}{c|}{\np{0.07}}&
\multicolumn{1}{c|}{\np{0.08}}&
\multicolumn{1}{c|}{\np{0.09}}\\\hline
% there is surely a way to automatize this...
  \myrow{0.0}\\
  \myrow{0.1}\\
  \myrow{0.2}\\
  \myrow{0.3}\\
  \myrow{0.4}\\
  \myrow{0.5}\\
  \myrow{0.6}\\
  \myrow{0.7}\\
  \myrow{0.8}\\
  \myrow{0.9}\\\hline
  \myrow{1.0}\\
  \myrow{1.1}\\
  \myrow{1.2}\\
  \myrow{1.3}\\
  \myrow{1.4}\\
  \myrow{1.5}\\
  \myrow{1.6}\\
  \myrow{1.7}\\
  \myrow{1.8}\\
  \myrow{1.9}\\\hline
  \myrow{2.0}\\
  \myrow{2.1}\\
  \myrow{2.2}\\
  \myrow{2.3}\\
  \myrow{2.4}\\
  \myrow{2.5}\\
  \myrow{2.6}\\
  \myrow{2.7}\\
  \myrow{2.8}\\
  \myrow{2.9}\\\hline
  \myrow{3.0}\\
  \myrow{3.1}\\
  \myrow{3.2}\\
  \myrow{3.3}\\
  \myrow{3.4}\\
  \myrow{3.5}\\
  \myrow{3.6}\\
  \myrow{3.7}\\
  \myrow{3.8}\\
  \myrow{3.9}\\\hline
  \myrow{4.0}%\\
  % \myrow{4.1}\\
  % \myrow{4.2}\\
  % \myrow{4.3}\\
  % \myrow{4.4}\\
  % \myrow{4.5}\\
  % \myrow{4.6}\\
  % \myrow{4.7}\\
  % \myrow{4.8}\\
  % \myrow{4.9}
\end{tabular}
\endgroup

\end{document}

table of standard normal distribution

6
  • Nice! - smarter than wikipedia :() Commented 23 hours ago
  • @cls ;-) The initial version of my answer used subsn() as in OP in \xintNewFloatExpr but I have realized since this is inefficient because the value of "h" in terms of "x" will be recalculated at each occurrence of "h" in a power "h^2", "h^3", etc... The new version uses \xintdeffloatfunc for a two-variable function as auxiliary. Commented 23 hours ago
  • update: the problem with subsn() is also there if one uses it inside \xintdeffloatfunc it is not only with \xintNewFloatExpr. Looks like an intrinsic limitation of xint regarding to this subsn() thing ! Used directly it makes the substitution but inside a function it does not know to compute only once the substituted value... probably an issue to raise with xint maintainer. Commented 22 hours ago
  • 1
    Just one very important correction: The \mathrm{d}t should not be part of the exponent. Commented 20 hours ago
  • 1
    @jps oops thanks a lot! Never could complete my calculus education... Commented 19 hours ago
8

You can expand the value before you hand it to \eval*. The following does so by defining a new command that expands its mandatory argument once if the starred version was used:

% Source - https://tex.stackexchange.com/q/758979
% Posted by cis, modified by community. See post 'Timeline' for change history
% Retrieved 2026-01-30, License - CC BY-SA 4.0

\documentclass[margin=10mm, varwidth]{standalone}
\usepackage{numerica}
\NewDocumentCommand\MaybeExpandOnce{mm}
  {\IfBooleanT{#1}{\edef\ProcessedArgument{\unexpanded\expandafter{#2}}}}
\NewDocumentCommand\evalNormalDist{s >{\MaybeExpandOnce{#1}}m O{5}}
  {%
    \eval*
      {%
        0.5 (2-h \exp(
        -0.5 x^2 -1.26551223
        +1.00002368 h +0.37409196 h^2
        +0.09678418 h^3 -0.18628806 h^4
        +0.27886807 h^5 -1.13520398 h^6
        +1.48851587* h^7 -0.82215223 h^8
        +0.17087277 h^9))
      }
      [
        h=1/(1+0.25*\sqrt{2}*x),  x={#2}% cannot use \x here :(
      ]
      [#3]%
  }

\begin{document}
\section{Test with \texttt{numerica}}
\xdef\x{0.61}
sn(\x) = \evalNormalDist*{\x}
\section{Test with \texttt{xint}}
... ? ....
\end{document}

Alternative solution: Use l3fp and define a function in it (well, two actually, to only calculate h once):

% Source - https://tex.stackexchange.com/q/758979
% Posted by cis, modified by community. See post 'Timeline' for change history
% Retrieved 2026-01-30, License - CC BY-SA 4.0

\documentclass[margin=10mm, varwidth]{standalone}

\ExplSyntaxOn
\fp_new_function:n { Phi }
\fp_new_function:n { PhiAux }
\fp_set_function:nnn { Phi } { x } { PhiAux(x, 1/(1 + 0.25 * sqrt(2) * x)) }
\fp_set_function:nnn { PhiAux } { x, h }
  {
    0.5 (2-h * exp(
    -0.5 x^2 -1.26551223
    % Horner's method for the polynomial is faster
    + h *( 1.00002368
    + h *( 0.37409196
    + h *( 0.09678418
    + h *(-0.18628806
    + h *( 0.27886807
    + h *(-1.13520398
    + h *( 1.48851587
    + h *(-0.82215223
    + h *  0.17087277
    ))))))))))
  }
\ExplSyntaxOff

\begin{document}
\section{Test with \texttt{l3fp}}
\xdef\x{0.61}
sn(\x) = \fpeval{round(Phi(\x), 5)}
\end{document}
4
  • Yes, that solves it in the sense of task setter. :() Commented yesterday
  • +1. I injected your l3fp code in my answer and used \NewDocumentCommand\SN{O{5} m}{\np{\fpeval{round(Phi(#2), #1)}}} (so \np from numprint is used in both cases for fair comparison) A bit to my surprise, it seems pdflatex takes about 5% more time with the l3fp code than with the xint one. Apart from that it seems \fpeval trims trailing zero and I don't know how to reinject them. Commented 19 hours ago
  • @user691586 well, l3fp isn't know to be the fastest, it's known to be one of the most precise :) If you aim for speed consider rewriting the polynomial using Horner's method. If I do that for your xintexpr based document compiling it takes 2.9s, while after sticking my l3fp-function into your \SN the entire document took 1.7s. So in this case l3fp was faster for this. Without Horner's l3fp took 4.0s and xintexpr only 3.8s. Commented 14 hours ago
  • @jps thanks for these interesting timings... maybe xint is a bit more nimble with powers, which Horner's method avoids. It is possible that for algebra l3fp is only a bit faster than xint, for the math functions it is more convincingly faster which however is not completely surprising as xint is multi-precision, not only 16 decimal digits. Commented 12 hours ago
0

you want a reusable macro for Standard Normal calculations in LaTeX. Since LaTeX doesn’t handle floating-point arithmetic natively, using a package like xintexpr or xint is usually the easiest approach. These allow you to define a macro that computes the result dynamically, so you can input any value and get the corresponding probability without manual calculation. Many find xintexpr simpler and fully compatible with pdflatex.

New contributor
James David is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
3
  • OK. Could you show an example with xint(expr)? Commented yesterday
  • 6
    @JamesDavid You might want to have a look at tex.stackexchange.com/help/gen-ai-policy Commented yesterday
  • The AI does not seem to know about LaTeX3 powerful built-in \fpeval. Commented 19 hours ago

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.