Skip to content

Commit 88dda01

Browse files
committed
Update after feedback
1 parent a2b40d2 commit 88dda01

File tree

1 file changed

+55
-43
lines changed

1 file changed

+55
-43
lines changed

lib/stdlib/src/rand.erl

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,17 @@ PRNGs in general, and so the algorithms in this module, are mostly used
4040
for test and simulation. They are designed for good statistical
4141
quality and high generation speed.
4242

43-
An generator algorithm, for each iteration, takes a state as input
43+
A generator algorithm, for each iteration, takes a state as input
4444
and produces a raw pseudo random number and a new state to be used
4545
for the next iteration.
4646

4747
A particular state always produces the same number and new state.
4848
The initial state is produced from a [seed](`seed/1`).
4949
This makes it possible to repeat for example a simulation with the same
5050
random number sequence, by re-using the same seed.
51+
There are also the functions `export_seed/0` and `export_seed_s/1`
52+
that capture the PRNG state in an `t:export_state/0`,
53+
that can be used to start from a known state.
5154

5255
This property, and others, make the algorithms in this module
5356
unsuitable for cryptographical applications, but in the `m:crypto` module
@@ -56,19 +59,19 @@ there are suitable generators, for this module's
5659
See `crypto:rand_seed_s/0` and `crypto:rand_seed_alg_s/1`.
5760

5861
At the end of this module documentation there are some
59-
[niche algorithms](#niche-algorithms) that does not use
62+
[niche algorithms](#niche-algorithms) that do not use
6063
this module's normal [plug-in framework](#plug-in-framework).
61-
They may be useful for special purposes like fast generation
64+
They are useful for special purposes like fast generation
6265
when quality is not essential, for seeding other generators, and such.
6366

6467
[](){: #plug-in-framework } Plug-in framework
6568
---------------------------------------------
6669

6770
The raw pseudo random numbers produced by the base generators
68-
are only suitable in some cases such as power of two ranges
71+
are only appropriate in some cases such as power of two ranges
6972
less than the generator size, and some have quirks,
7073
for example weak low bits. Therefore, the Plug-in Framework
71-
implements a common [API](#plug-in-framework-api) to all base generator,
74+
implements a common [API](#plug-in-framework-api) for all base generators,
7275
that add essential or useful funcionality:
7376

7477
* Keeping the generator [state](`seed/1`) in the process dictionary.
@@ -93,7 +96,7 @@ A generator has to be initialized. This is done by one of the
9396
`seed/1` or `seed_s/1` functions, which also select which
9497
[algorithm](#algorithms) to use. The `seed/1` functions
9598
store the generator and state in the process dictionary,
96-
while the `seed_s/1` functions do not, which requires
99+
while the `seed_s/1` functions only return the state, which requires
97100
the calling code to handle the state and updates to it.
98101

99102
The seed functions that do not have a `Seed` value as an argument
@@ -113,7 +116,8 @@ Sibling functions without that suffix take an implicit state from
113116
and store the new state in the process dictionary, and only return
114117
their "interesting " output value. If the process dictionary
115118
does not contain a state, [`seed(default)`](`seed/1`)
116-
is implicitly called to create an automatic seed as initial state.
119+
is implicitly called to create an automatic seed for the
120+
[_default algorithm_](#default-algorithm) as initial state.
117121

118122
#### _Usage_
119123

@@ -123,7 +127,7 @@ functions, which also selects a PRNG algorithm.
123127
Then call a [Plug-in framework API](#plug-in-framework-api) function
124128
either with an explicit state from the seed function
125129
and use the returned new state in the next call,
126-
or call an API function without an explicit state
130+
or call an API function without an explicit state argument
127131
to operate on the state in the process dictionary.
128132

129133
#### _Examples_
@@ -132,7 +136,7 @@ to operate on the state in the process dictionary.
132136
%% Generate two uniformly distibuted floating point numbers.
133137
%%
134138
%% By not calling a [seed](`seed/1`) function, this uses
135-
%% the genarator state and algorithm in the process dictinary.
139+
%% the generator state and algorithm in the process dictionary.
136140
%% If there is no state there, [`seed(default)`](`seed/1`)
137141
%% is implicitly called first:
138142
%%
@@ -159,7 +163,7 @@ true
159163
%% with an automatic default seed, then generate
160164
%% a floating point number:
161165
%%
162-
5> _ = rand:seed(exro928ss).
166+
5> rand:seed(exro928ss).
163167
6> R2 = rand:uniform(),
164168
is_float(R2) andalso 0.0 =< R2 andalso R2 < 1.0.
165169
true
@@ -168,12 +172,11 @@ true
168172
%% with a specified seed, then generate
169173
%% a floating point number:
170174
%%
171-
7> _ = rand:seed(exro928ss, 123456789).
172-
8> R3 = rand:uniform(),
173-
is_float(R3) andalso 0.0 =< R3 andalso R3 < 1.0.
174-
true
175+
7> rand:seed(exro928ss, 123456789).
176+
8> R3 = rand:uniform().
177+
0.48303622772415256
175178

176-
%% Select and initialize a specified algorithm,
179+
%% Select and initialize a specific algorithm,
177180
%% with an automatic default seed, using the functional API
178181
%% with explicit generator state, then generate
179182
%% two floating point numbers.
@@ -196,7 +199,7 @@ true
196199
true
197200

198201
%% Generate a normal distribution number
199-
%% with with mean -3 and variance 0.5:
202+
%% with mean -3 and variance 0.5:
200203
%%
201204
14> {ND0, S4} = rand:normal_s(-3, 0.5, S3),
202205
is_float(ND0).
@@ -236,9 +239,10 @@ per generator bit.
236239

237240
By using a jump function instead of starting several generators
238241
from different seeds it is assured that the generated sequences
239-
does not overlap. Two different seeds may accidentally start
240-
the generators in sequence positions that are close to each other,
241-
but a jump function jumps to a sequence position very far ahead.
242+
do not overlap. The alternative of using different seeds
243+
may accidentally start the generators in sequence positions
244+
that are close to each other, but a jump function jumps
245+
to a sequence position very far ahead.
242246

243247
To create numbers with normal distribution the
244248
[Ziggurat Method by Marsaglia and Tsang](http://www.jstatsoft.org/v05/i08)
@@ -248,9 +252,9 @@ The following algorithms are provided:
248252

249253
- **`exsss`**, the [_default algorithm_](#default-algorithm)
250254
*(Since OTP 22.0)*
251-
Xorshift116\*\*, 58 bits precision and period of 2^116-1
255+
Xorshift116\*\*, 58 bits precision and period of 2^116-1.
252256

253-
Jump function: equivalent to 2^64 calls
257+
Jump function: equivalent to 2^64 calls.
254258

255259
This is the Xorshift116 generator combined with the StarStar scrambler from
256260
the 2018 paper by David Blackman and Sebastiano Vigna:
@@ -265,9 +269,9 @@ The following algorithms are provided:
265269
its statistical qualities.
266270

267271
- **`exro928ss`** *(Since OTP 22.0)*
268-
Xoroshiro928\*\*, 58 bits precision and a period of 2^928-1
272+
Xoroshiro928\*\*, 58 bits precision and a period of 2^928-1.
269273

270-
Jump function: equivalent to 2^512 calls
274+
Jump function: equivalent to 2^512 calls.
271275

272276
This is a 58 bit version of Xoroshiro1024\*\*, from the 2018 paper by
273277
David Blackman and Sebastiano Vigna:
@@ -280,25 +284,29 @@ The following algorithms are provided:
280284
Many thanks to Sebastiano Vigna for his help with the 58 bit adaption.
281285

282286
- **`exrop`** *(Since OTP 20.0)*
283-
Xoroshiro116+, 58 bits precision and period of 2^116-1
287+
Xoroshiro116+, 58 bits precision and period of 2^116-1.
284288

285-
Jump function: equivalent to 2^64 calls
289+
Jump function: equivalent to 2^64 calls.
286290

287291
- **`exs1024s`** *(Since OTP 20.0)*
288292
Xorshift1024\*, 64 bits precision and a period of 2^1024-1
289293

290-
Jump function: equivalent to 2^512 calls
294+
Jump function: equivalent to 2^512 calls.
295+
296+
Since this generator operates on 64-bit integers that are bignums
297+
on 64 bit platforms, it is much slower than `exro928ss` above.
291298

292299
- **`exsp`** *(Since OTP 20.0)*
293300
Xorshift116+, 58 bits precision and period of 2^116-1
294301

295-
Jump function: equivalent to 2^64 calls
302+
Jump function: equivalent to 2^64 calls.
296303

297304
This is a corrected version of a previous
298305
[_default algorithm_](#default-algorithm) (`exsplus`, _deprecated_),
299306
that was superseded by Xoroshiro116+ (`exrop`). Since this algorithm
300-
does not use rotate it executes a little (say < 15%) faster than `exrop`
301-
(that has to do a 58 bit rotate, for which there is no native instruction).
307+
does not use rotate operations it executes a little (say < 15%) faster
308+
than `exrop` (that has to do a 58 bit rotate,
309+
for which there is no native instruction).
302310
See the [algorithms' homepage](http://xorshift.di.unimi.it).
303311

304312
[](){: #default-algorithm }
@@ -310,7 +318,9 @@ required, ensure to always use `seed/1` to initialize the state.
310318

311319
Which algorithm that is the default may change between Erlang/OTP releases,
312320
and is selected to be one with high speed, small state and "good enough"
313-
statistical properties.
321+
statistical properties. So to ensure that the same sequence is reproduced
322+
on a later Erlang/OTP release, use a `seed/2` or `seed_s/2` to select
323+
both a specific algorithm and the seed value.
314324

315325
#### Old Algorithms
316326

@@ -1050,7 +1060,7 @@ The concept implicates that the probability to get exactly zero is extremely
10501060
low; so low that this function in fact never returns `0.0`.
10511061
The smallest number that it *might* return is `DBL_MIN`,
10521062
which is `2.0^(-1022)`. However, the generators in this module
1053-
has thechnical limitations on how many zero words in a row they
1063+
have technical limitations on how many zero words in a row they
10541064
*can* return, which limits the number of leading zeros
10551065
that *can* be generated, which sets an upper limit for the smallest
10561066
generated number, that is still extremely small.
@@ -1062,7 +1072,7 @@ never returns exactly `0.0` is impossible to observe.
10621072

10631073
For all sub ranges `N*2.0^(-53) =< X < (N+1)*2.0^(-53)` where
10641074
`0 =< integer(N) < 2.0^53`, the probability to generate a number
1065-
in a sub range range is the same, very much like the numbers generated by
1075+
in a sub range is the same, very much like the numbers generated by
10661076
`uniform_s/1`.
10671077

10681078
Having to generate extra random bits for occasional small numbers
@@ -1251,7 +1261,7 @@ as required to compose the `t:binary/0`. Returns the generated
12511261
>
12521262
> The `m:crypto` module contains a function `crypto:strong_rand_bytes/1`
12531263
> that does the same thing, but cryptographically secure.
1254-
> It is pretty fast and effective on modern systems.
1264+
> It is pretty fast and efficient on modern systems.
12551265
>
12561266
> This function, however, offers the possibility to reproduce
12571267
> a byte sequence by re-using seed, which a cryptographically secure
@@ -1261,10 +1271,10 @@ as required to compose the `t:binary/0`. Returns the generated
12611271
> random integers, thus has to create bytes from integers,
12621272
> it becomes rather slow.
12631273
>
1264-
> Particularly ineffective and slow is to use
1274+
> Particularly inefficient and slow is to use
12651275
> a [`rand` plug-in generator](#plug-in-framework) from `m:crypto`
12661276
> such as `crypto:rand_seed_s/0` to call this function for generating
1267-
> bytes. Since it in that case is not possible to reproduce
1277+
> bytes. Since in that case it is not possible to reproduce
12681278
> the byte sequence anyway; it is better to use
12691279
> `crypto:strong_rand_bytes/1` directly.
12701280
""".
@@ -2462,9 +2472,14 @@ adding up to 59 bits, which is not a bignum (on a 64-bit VM ):
24622472
""".
24632473
-doc(#{group => <<"Niche algorithms API">>,since => <<"OTP 25.0">>}).
24642474
-spec mwc59_value(CX :: mwc59_state()) -> V :: 0..?MASK(59).
2465-
mwc59_value(CX) when is_integer(CX), 1 =< CX, CX < ?MWC59_P ->
2466-
CX2 = CX bxor ?BSL(59, CX, ?MWC59_XS1),
2467-
CX2 bxor ?BSL(59, CX2, ?MWC59_XS2).
2475+
-define(
2476+
mwc59_value(CX0, CX1),
2477+
begin
2478+
CX1 = (CX0) bxor ?BSL(59, (CX0), ?MWC59_XS1),
2479+
CX1 bxor ?BSL(59, CX1, ?MWC59_XS2)
2480+
end).
2481+
mwc59_value(CX0) when is_integer(CX0), 1 =< CX0, CX0 < ?MWC59_P ->
2482+
?mwc59_value(CX0, CX1).
24682483

24692484
-doc """
24702485
Calculate a scrambled `t:float/0` from a [MWC59 state](`t:mwc59_state/0`).
@@ -2477,11 +2492,8 @@ The generator state is scrambled as with
24772492
""".
24782493
-doc(#{group => <<"Niche algorithms API">>,since => <<"OTP 25.0">>}).
24792494
-spec mwc59_float(CX :: mwc59_state()) -> V :: float().
2480-
mwc59_float(CX1) when is_integer(CX1), 1 =< CX1, CX1 < ?MWC59_P ->
2481-
CX = ?MASK(53, CX1),
2482-
CX2 = CX bxor ?BSL(53, CX, ?MWC59_XS1),
2483-
CX3 = CX2 bxor ?BSL(53, CX2, ?MWC59_XS2),
2484-
CX3 * ?TWO_POW_MINUS53.
2495+
mwc59_float(CX0) when is_integer(CX0), 1 =< CX0, CX0 < ?MWC59_P ->
2496+
?MASK(53, ?mwc59_value(CX0, CX1)) * ?TWO_POW_MINUS53.
24852497

24862498
-doc """
24872499
Create a [MWC59 generator state](`t:mwc59_state/0`).

0 commit comments

Comments
 (0)