[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