[latex3-commits] [git/LaTeX3-latex3-latex3] master: Fix bug in \__kernel_randint:n (hence in \int_rand:nn) (142196a)
Bruno Le Floch
bruno at le-floch.fr
Mon May 7 18:41:24 CEST 2018
Repository : https://github.com/latex3/latex3
On branch : master
Link : https://github.com/latex3/latex3/commit/142196acec5b64cf6af701a07498e43eabd5e7c1
>---------------------------------------------------------------
commit 142196acec5b64cf6af701a07498e43eabd5e7c1
Author: Bruno Le Floch <bruno at le-floch.fr>
Date: Mon May 7 12:41:24 2018 -0400
Fix bug in \__kernel_randint:n (hence in \int_rand:nn)
>---------------------------------------------------------------
142196acec5b64cf6af701a07498e43eabd5e7c1
l3kernel/l3fp-random.dtx | 37 ++++++++++++++++++++-----------------
l3kernel/l3intarray.dtx | 11 ++++++-----
l3kernel/l3kernel-functions.dtx | 14 +++++++-------
3 files changed, 33 insertions(+), 29 deletions(-)
diff --git a/l3kernel/l3fp-random.dtx b/l3kernel/l3fp-random.dtx
index c319367..e0c2285 100644
--- a/l3kernel/l3fp-random.dtx
+++ b/l3kernel/l3fp-random.dtx
@@ -205,13 +205,13 @@
% $X_0$ term has a tiny effect so we ignore it and we can compute
% $R\times Y/2^{28}$ much more directly by $\operatorname{random}(R)$.
% \begin{itemize}
-% \item If $R \leq 2^{17}$ then return
-% $\meta{min} + \operatorname{ediv}(R\operatorname{random}(2^{14}) +
-% \operatorname{random}(R) - 2^{13}, 2^{14})$. The shift by $2^{13}$
-% converts \eTeX{} division to truncated division. The bound on $R$
-% ensures that the number obtained before the shift and division is at
-% most $2^{17}(2^{14}-1)+(2^{17}-1)=\cs{c_max_int}$. The
-% non-uniformity is at most of order $2^{17}/2^{42} = 2^{-25}$.
+% \item If $R \leq 2^{17}-1$ then return
+% $\operatorname{ediv}(R\operatorname{random}(2^{14}) +
+% \operatorname{random}(R) + 2^{13}, 2^{14}) - 1 + \meta{min}$. The
+% shifts by $2^{13}$ and $-1$ convert \eTeX{} division to truncated
+% division. The bound on $R$ ensures that the number obtained after
+% the shift is less than \cs{c_max_int}. The non-uniformity is at
+% most of order $2^{17}/2^{42} = 2^{-25}$.
% \item Split $R=R_2\times 2^{28}+R_1\times 2^{14}+R_0$, where
% $R_2\in [0,15]$. Compute
% $\meta{min} + R_2 X_1 2^{14} + (R_2 Y_1 + R_1 X_1) +
@@ -250,7 +250,7 @@
% in an integer expression in which \meta{second} is eventually added
% (or not).
% \item To get a floating point number in $[0,1)$ just call the
-% $R=10000\leq 2^{17}$ procedure above to produce four blocks of four
+% $R=10000\leq 2^{17}-1$ procedure above to produce four blocks of four
% digits.
% \item To get an integer floating point number in a range (whose size
% can be up to $2\times 10^{16}-1$), work with fixed-point numbers:
@@ -260,23 +260,26 @@
% \end{itemize}
%
% \begin{variable}{\c__kernel_randint_max_int}
-% Constant equal to $2^{17}$, the maximal size of a range that
+% Constant equal to $2^{17}-1$, the maximal size of a range that
% \cs{int_range:nn} can do with its \enquote{simple} algorithm.
% \begin{macrocode}
- \int_const:Nn \c__kernel_randint_max_int { 131072 }
+ \int_const:Nn \c__kernel_randint_max_int { 131071 }
% \end{macrocode}
% \end{variable}
%
% \begin{macro}[EXP]{\__kernel_randint:n}
% Used in an integer expression, \cs{__kernel_randint:n} |{|$R$|}|
% gives a random number
-% $\lfloor(R\operatorname{random}(2^{14}) +
-% \operatorname{random}(R))/2^{14}\rfloor$ that is in $[0,R-1]$.
+% $1+\lfloor(R\operatorname{random}(2^{14}) +
+% \operatorname{random}(R))/2^{14}\rfloor$ that is in $[1,R]$.
+% Previous code was computing $\lfloor p/2^{14}\rfloor$ as
+% $\operatorname{ediv}(p-2^{13},2^{14})$ but that wrongly gives $-1$
+% for $p=0$.
% \begin{macrocode}
\cs_new:Npn \__kernel_randint:n #1
{
- (#1 * \tex_uniformdeviate:D 1 6384
- + \tex_uniformdeviate:D #1 - 8192 ) / 16384
+ (#1 * \tex_uniformdeviate:D 16384
+ + \tex_uniformdeviate:D #1 + 8192 ) / 16384
}
% \end{macrocode}
% \end{macro}
@@ -295,7 +298,7 @@
{
#1
\exp_after:wN \@@_rand_myriads_get:w
- \int_value:w \@@_int_eval:w 10000 +
+ \int_value:w \@@_int_eval:w 9999 +
\__kernel_randint:n { 10000 }
\@@_rand_myriads_loop:w
}
@@ -508,9 +511,9 @@
\fi:
\c__kernel_randint_max_int
\@@_int_eval_end:
- #1 +
\__kernel_randint:n
{ \@@_int_eval:w #2 - #1 + 1 \@@_int_eval_end: }
+ - 1 + #1
\else:
\__kernel_randint:nn {#1} {#2}
\fi:
@@ -620,7 +623,7 @@
\if_int_compare:w #1 > \c__kernel_randint_max_int
\__kernel_randint:nn { 1 } {#1}
\else:
- 1 + \__kernel_randint:n {#1}
+ \__kernel_randint:n {#1}
\fi:
\fi:
}
diff --git a/l3kernel/l3intarray.dtx b/l3kernel/l3intarray.dtx
index 5b2ea16..f422ef6 100644
--- a/l3kernel/l3intarray.dtx
+++ b/l3kernel/l3intarray.dtx
@@ -461,10 +461,11 @@
% avoid a spurious \enquote{at position \texttt{\#1}} part in the
% error message. Then calculate the number of choices: this is at
% most $(2^{30}-1)-(-(2^{30}-1))+1=2^{31}-1$, which just barely does
-% not overflow. For small ranges use \cs{__kernel_randint:n},
-% otherwise \cs{__kernel_randint:nn}.
-% Finally, if there are no random numbers do not define any of the
-% auxiliaries.
+% not overflow. For small ranges use \cs{__kernel_randint:n} (making
+% sure to subtract~$1$ \emph{before} adding the random number to the
+% \meta{min}, to avoid overflow when \meta{min} or \meta{max} are
+% $\pm\cs{c_max_int}$), otherwise \cs{__kernel_randint:nn}. Finally,
+% if there are no random numbers do not define any of the auxiliaries.
% \begin{macrocode}
\cs_new_protected:Npn \intarray_gset_rand:Nn #1
{ \intarray_gset_rand:Nnn #1 { 1 } }
@@ -510,7 +511,7 @@
}
{
\exp_stop_f:
- \int_eval:n { #3 + \__kernel_randint:n {#2} }
+ \int_eval:n { \__kernel_randint:n {#2} - 1 + #3 }
}
}
}
diff --git a/l3kernel/l3kernel-functions.dtx b/l3kernel/l3kernel-functions.dtx
index b857e18..90cf558 100644
--- a/l3kernel/l3kernel-functions.dtx
+++ b/l3kernel/l3kernel-functions.dtx
@@ -443,17 +443,17 @@
%
% \begin{variable}{\c__kernel_randint_max_int}
% Maximal allowed argument to \cs{__kernel_randint:n}. Equal to
-% $2^{17}$.
+% $2^{17}-1$.
% \end{variable}
%
% \begin{function}{\__kernel_randint:n}
% \begin{syntax}
-% \cs{__kernel_randint:n} \Arg{choices}
+% \cs{__kernel_randint:n} \Arg{max}
% \end{syntax}
% Used in an integer expression this gives a pseudo-random number
-% between $0$ and $\meta{choices} - 1$ included. One must have
-% $\meta{choices}\leq 2^{17}$. The \meta{choices} must be suitable
-% for \cs{int_value:w} (and any \cs{int_eval:w} must be terminated by
+% between $1$ and $\meta{max}$ included. One must have
+% $\meta{max}\leq 2^{17}-1$. The \meta{max} must be suitable for
+% \cs{int_value:w} (and any \cs{int_eval:w} must be terminated by
% \cs{scan_stop:} or equivalent).
% \end{function}
%
@@ -466,8 +466,8 @@
% \meta{max} must be suitable for \cs{int_value:w} (and any
% \cs{int_eval:w} must be terminated by \cs{scan_stop:} or
% equivalent). For small ranges
-% $R=\meta{max}-\meta{min}+1\leq 2^{17}$,
-% $\meta{min} + \cs{__kernel_randint:n} |{|R|}|$ is faster.
+% $R=\meta{max}-\meta{min}+1\leq 2^{17}-1$,
+% $\meta{min} - 1 + \cs{__kernel_randint:n} |{|R|}|$ is faster.
% \end{function}
%
% \begin{function}{\__kernel_register_show:N, \__kernel_register_show:c}
More information about the latex3-commits
mailing list