From a24283e69e89f491c1644985b5caae1c5111a9da Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 3 Nov 2020 12:19:00 -0500 Subject: [PATCH] Clean up style of Random.cs (#44195) No functional changes. --- .../System.Private.CoreLib/src/System/Random.cs | 293 +++++++++------------ 1 file changed, 118 insertions(+), 175 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs index 2e8f249..1cf0396 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Random.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs @@ -5,250 +5,193 @@ namespace System { public class Random { - // - // Private Constants - // - private const int MBIG = int.MaxValue; - private const int MSEED = 161803398; - - // - // Member Variables - // + private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed()); + [ThreadStatic] + private static Random? t_threadRandom; + + private readonly int[] _seedArray = new int[56]; private int _inext; private int _inextp; - private readonly int[] _seedArray = new int[56]; - - // - // Public Constants - // - - // - // Native Declarations - // - // - // Constructors - // - - /*========================================================================================= - **Action: Initializes a new instance of the Random class, using a default seed value - ===========================================================================================*/ - public Random() - : this(GenerateSeed()) + public Random() : this(ThreadStaticRandom.Next()) { } - /*========================================================================================= - **Action: Initializes a new instance of the Random class, using a specified seed value - ===========================================================================================*/ public Random(int Seed) { - int ii = 0; - int mj, mk; + // Initialize seed array. - // Initialize our Seed array. int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed); - mj = MSEED - subtraction; + int mj = 161803398 - subtraction; // magic number based on Phi (golden ratio) _seedArray[55] = mj; - mk = 1; + int mk = 1; + + int ii = 0; for (int i = 1; i < 55; i++) - { // Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position. - if ((ii += 21) >= 55) ii -= 55; + { + // The range [1..55] is special (Knuth) and so we're wasting the 0'th position. + if ((ii += 21) >= 55) + { + ii -= 55; + } + _seedArray[ii] = mk; mk = mj - mk; - if (mk < 0) mk += MBIG; + if (mk < 0) + { + mk += int.MaxValue; + } + mj = _seedArray[ii]; } + for (int k = 1; k < 5; k++) { for (int i = 1; i < 56; i++) { int n = i + 30; - if (n >= 55) n -= 55; + if (n >= 55) + { + n -= 55; + } + _seedArray[i] -= _seedArray[1 + n]; - if (_seedArray[i] < 0) _seedArray[i] += MBIG; + if (_seedArray[i] < 0) + { + _seedArray[i] += int.MaxValue; + } } } - _inextp = 21; - } - // - // Package Private Methods - // - - /*====================================Sample==================================== - **Action: Return a new random number [0..1) and reSeed the Seed array. - **Returns: A double [0..1) - **Arguments: None - **Exceptions: None - ==============================================================================*/ - protected virtual double Sample() - { - // Including this division at the end gives us significantly improved - // random number distribution. - return InternalSample() * (1.0 / MBIG); + _inextp = 21; } - private int InternalSample() - { - int retVal; - int locINext = _inext; - int locINextp = _inextp; - - if (++locINext >= 56) locINext = 1; - if (++locINextp >= 56) locINextp = 1; - - retVal = _seedArray[locINext] - _seedArray[locINextp]; + protected virtual double Sample() => + // Including the division at the end gives us significantly improved random number distribution. + InternalSample() * (1.0 / int.MaxValue); - if (retVal == MBIG) retVal--; - if (retVal < 0) retVal += MBIG; + public virtual int Next() => InternalSample(); - _seedArray[locINext] = retVal; - - _inext = locINext; - _inextp = locINextp; + public virtual int Next(int maxValue) + { + if (maxValue < 0) + { + throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue))); + } - return retVal; + return (int)(Sample() * maxValue); } - [ThreadStatic] - private static Random? t_threadRandom; - private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed()); - - /*=====================================GenerateSeed===================================== - **Returns: An integer that can be used as seed values for consecutively - creating lots of instances on the same thread within a short period of time. - ========================================================================================*/ - private static int GenerateSeed() + public virtual int Next(int minValue, int maxValue) { - Random? rnd = t_threadRandom; - if (rnd == null) + if (minValue > maxValue) { - int seed; - lock (s_globalRandom) - { - seed = s_globalRandom.Next(); - } - rnd = new Random(seed); - t_threadRandom = rnd; + throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue))); } - return rnd.Next(); - } - /*==================================GenerateGlobalSeed==================================== - **Action: Creates a number to use as global seed. - **Returns: An integer that is safe to use as seed values for thread-local seed generators. - ==========================================================================================*/ - private static unsafe int GenerateGlobalSeed() - { - int result; - Interop.GetRandomBytes((byte*)&result, sizeof(int)); - return result; + long range = (long)maxValue - minValue; + return range <= int.MaxValue ? + (int)(Sample() * range) + minValue : + (int)((long)(GetSampleForLargeRange() * range) + minValue); } - // - // Public Instance Methods - // + public virtual double NextDouble() => Sample(); - /*=====================================Next===================================== - **Returns: An int [0..int.MaxValue) - **Arguments: None - **Exceptions: None. - ==============================================================================*/ - public virtual int Next() + public virtual void NextBytes(byte[] buffer) { - return InternalSample(); + if (buffer is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.buffer); + } + + for (int i = 0; i < buffer.Length; i++) + { + buffer[i] = (byte)InternalSample(); + } } - private double GetSampleForLargeRange() + public virtual void NextBytes(Span buffer) { - // The distribution of double value returned by Sample - // is not distributed well enough for a large range. - // If we use Sample for a range [int.MinValue..int.MaxValue) - // We will end up getting even numbers only. - - int result = InternalSample(); - // Note we can't use addition here. The distribution will be bad if we do that. - bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample - if (negative) + for (int i = 0; i < buffer.Length; i++) { - result = -result; + buffer[i] = (byte)Next(); } - double d = result; - d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1) - d /= 2 * (uint)int.MaxValue - 1; - return d; } - /*=====================================Next===================================== - **Returns: An int [minvalue..maxvalue) - **Arguments: minValue -- the least legal value for the Random number. - ** maxValue -- One greater than the greatest legal return value. - **Exceptions: None. - ==============================================================================*/ - public virtual int Next(int minValue, int maxValue) + private int InternalSample() { - if (minValue > maxValue) + int locINext = _inext; + if (++locINext >= 56) { - throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue))); + locINext = 1; } - long range = (long)maxValue - minValue; - if (range <= int.MaxValue) + int locINextp = _inextp; + if (++locINextp >= 56) { - return (int)(Sample() * range) + minValue; + locINextp = 1; } - else + + int retVal = _seedArray[locINext] - _seedArray[locINextp]; + + if (retVal == int.MaxValue) + { + retVal--; + } + if (retVal < 0) { - return (int)((long)(GetSampleForLargeRange() * range) + minValue); + retVal += int.MaxValue; } + + _seedArray[locINext] = retVal; + _inext = locINext; + _inextp = locINextp; + + return retVal; } - /*=====================================Next===================================== - **Returns: An int [0..maxValue) - **Arguments: maxValue -- One more than the greatest legal return value. - **Exceptions: None. - ==============================================================================*/ - public virtual int Next(int maxValue) + private static Random ThreadStaticRandom { - if (maxValue < 0) + get { - throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue))); + return t_threadRandom ??= CreateThreadStaticRandom(); + + static Random CreateThreadStaticRandom() + { + int seed; + lock (s_globalRandom) + { + seed = s_globalRandom.Next(); + } + + return new Random(seed); + } } - return (int)(Sample() * maxValue); } - /*=====================================Next===================================== - **Returns: A double [0..1) - **Arguments: None - **Exceptions: None - ==============================================================================*/ - public virtual double NextDouble() + private static unsafe int GenerateGlobalSeed() { - return Sample(); + int result; + Interop.GetRandomBytes((byte*)&result, sizeof(int)); + return result; } - /*==================================NextBytes=================================== - **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled. - **Returns:Void - **Arguments: buffer -- the array to be filled. - **Exceptions: None - ==============================================================================*/ - public virtual void NextBytes(byte[] buffer) + private double GetSampleForLargeRange() { - if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - for (int i = 0; i < buffer.Length; i++) - { - buffer[i] = (byte)InternalSample(); - } - } + // The distribution of the double returned by Sample is not good enough for a large range. + // If we use Sample for a range [int.MinValue..int.MaxValue), we will end up getting even numbers only. + int result = InternalSample(); - public virtual void NextBytes(Span buffer) - { - for (int i = 0; i < buffer.Length; i++) + // We can't use addition here: the distribution will be bad if we do that. + if (InternalSample() % 2 == 0) // decide the sign based on second sample { - buffer[i] = (byte)Next(); + result = -result; } + + double d = result; + d += int.MaxValue - 1; // get a number in range [0..2*int.MaxValue-1) + d /= 2u * int.MaxValue - 1; + return d; } } } -- 2.7.4