File 2964-Increase-mwc59-seed-range-to-58-bits.patch of Package erlang
From 6949861e00c48f5cae13218bd051a572850660a6 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Tue, 10 May 2022 16:25:10 +0200
Subject: [PATCH 4/8] Increase mwc59 seed range to 58 bits
To hash the seed into 58 bits and then add 1 is better than
hashing into 57 bits and then bor 1 bsl 58. 58 bits is more
familiar and twice as large.
---
lib/stdlib/doc/src/rand.xml | 4 ++--
lib/stdlib/src/rand.erl | 27 +++++++++++++++------------
lib/stdlib/test/rand_SUITE.erl | 10 +++++-----
3 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/lib/stdlib/doc/src/rand.xml b/lib/stdlib/doc/src/rand.xml
index 7eb6a164c8..8b9b924366 100644
--- a/lib/stdlib/doc/src/rand.xml
+++ b/lib/stdlib/doc/src/rand.xml
@@ -1064,8 +1064,8 @@ end.</pre>
<desc>
<p>
Returns a generator state <c><anno>CX</anno></c>.
- It is set it to <c><anno>S</anno></c>, after folding it
- into the state's range, if out of range.
+ <c><anno>S</anno></c> is hashed to create the generator state,
+ to avoid that similar seeds create similar sequences.
</p>
<p>
Without <c><anno>S</anno></c>,
diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl
index 14e4f7eb74..ac61acd955 100644
--- a/lib/stdlib/src/rand.erl
+++ b/lib/stdlib/src/rand.erl
@@ -1533,29 +1533,32 @@ mwc59_value(CX1) -> % when is_integer(CX1), 1 =< CX1, CX1 < ?MWC59_P ->
-spec mwc59_float(CX :: mwc59_state()) -> V :: float().
mwc59_float(CX1) ->
- mwc59_value(CX1) * (1/(1 bsl 59)).
+ CX = ?MASK(59, CX1),
+ CX2 = CX bxor ?BSL(59, CX, ?MWC59_XS1),
+ CX3 = CX2 bxor ?BSL(59, CX2, ?MWC59_XS2),
+ (CX3 bsr (59-53)) * ?TWO_POW_MINUS53.
-spec mwc59_seed() -> CX :: mwc59_state().
mwc59_seed() ->
{A1, A2, A3} = default_seed(),
- X1 = hash57(A1),
- X2 = hash57(A2),
- X3 = hash57(A3),
- ?BIT(58) bor (X1 bxor X2 bxor X3).
+ X1 = hash58(A1),
+ X2 = hash58(A2),
+ X3 = hash58(A3),
+ (X1 bxor X2 bxor X3) + 1.
--spec mwc59_seed(S :: 0..?MASK(57)) -> CX :: mwc59_state().
-mwc59_seed(S) when is_integer(S), 0 =< S, S =< ?MASK(57) ->
- ?BIT(58) bor hash57(S).
+-spec mwc59_seed(S :: 0..?MASK(58)) -> CX :: mwc59_state().
+mwc59_seed(S) when is_integer(S), 0 =< S, S =< ?MASK(58) ->
+ hash58(S) + 1.
%% Constants a'la SplitMix64, MurMurHash, etc.
%% Not that critical, just mix the bits using bijections
%% (reversible mappings) to not have any two user input seeds
%% become the same generator start state.
%%
-hash57(X) ->
- X0 = ?MASK(57, X),
- X1 = ?MASK(57, (X0 bxor (X0 bsr 29)) * 16#151afd7ed558ccd),
- X2 = ?MASK(57, (X1 bxor (X1 bsr 29)) * 16#0ceb9fe1a85ec53),
+hash58(X) ->
+ X0 = ?MASK(58, X),
+ X1 = ?MASK(58, (X0 bxor (X0 bsr 29)) * 16#351afd7ed558ccd),
+ X2 = ?MASK(58, (X1 bxor (X1 bsr 29)) * 16#0ceb9fe1a85ec53),
X2 bxor (X2 bsr 29).
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index e84689ea69..5f3df66bee 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -216,25 +216,25 @@ mwc59_api(Config) when is_list(Config) ->
error({bad_return, CX1})
catch
error : function_clause ->
- try rand:mwc59_seed(1 bsl 57) of
+ try rand:mwc59_seed(1 bsl 58) of
CX2 ->
error({bad_return, CX2})
catch
error : function_clause ->
- Seed = 324109835948422043,
+ Seed = 11213862807209314,
Seed = rand:mwc59_seed(1),
mwc59_api(Seed, 1000000)
end
end.
mwc59_api(CX0, 0) ->
- CX = 394988924775693874,
+ CX = 182322083224642863,
{CX, CX} = {CX0, CX},
V0 = rand:mwc59_value32(CX0),
- V = 3767127090,
+ V = 2905950767,
{V, V} = {V0, V},
W0 = rand:mwc59_value(CX0),
- W = 418709302640385298,
+ W = 269866568368142303,
{W, W} = {W0, W},
F0 = rand:mwc59_float(CX0),
F = (W bsr (59-53)) * (1 / (1 bsl 53)),
--
2.35.3