diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs b/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs
index 59bf7dab77c83c8e7ac56abe7fe000a7eb13a41e..8b4e14ebbc204cbc5b611dbd9641f52a078494d0 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Random.Xoshiro256StarStarImpl.cs
@@ -112,7 +112,7 @@ public override int Next(int maxValue)
public override int Next(int minValue, int maxValue)
{
- ulong exclusiveRange = (ulong)(maxValue - minValue);
+ ulong exclusiveRange = (ulong)((long)maxValue - minValue);
if (exclusiveRange > 1)
{
diff --git a/src/libraries/System.Private.CoreLib/src/System/Random.cs b/src/libraries/System.Private.CoreLib/src/System/Random.cs
index 64b9eb97c0971668fd7c8f660a2bb1d8d78462bb..21ea60e442abfc1aea014d281b7619f372a9c206 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Random.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Random.cs
@@ -52,7 +52,12 @@ protected private Random(bool isThreadSafeRandom)
/// Returns a non-negative random integer.
/// A 32-bit signed integer that is greater than or equal to 0 and less than .
- public virtual int Next() => _impl.Next();
+ public virtual int Next()
+ {
+ int result = _impl.Next();
+ AssertInRange(result, 0, int.MaxValue);
+ return result;
+ }
/// Returns a non-negative random integer that is less than the specified maximum.
/// The exclusive upper bound of the random number to be generated. must be greater than or equal to 0.
@@ -68,7 +73,9 @@ public virtual int Next(int maxValue)
ThrowMaxValueMustBeNonNegative();
}
- return _impl.Next(maxValue);
+ int result = _impl.Next(maxValue);
+ AssertInRange(result, 0, maxValue);
+ return result;
}
/// Returns a random integer that is within a specified range.
@@ -86,12 +93,19 @@ public virtual int Next(int minValue, int maxValue)
ThrowMinMaxValueSwapped();
}
- return _impl.Next(minValue, maxValue);
+ int result = _impl.Next(minValue, maxValue);
+ AssertInRange(result, minValue, maxValue);
+ return result;
}
/// Returns a non-negative random integer.
/// A 64-bit signed integer that is greater than or equal to 0 and less than .
- public virtual long NextInt64() => _impl.NextInt64();
+ public virtual long NextInt64()
+ {
+ long result = _impl.NextInt64();
+ AssertInRange(result, 0, long.MaxValue);
+ return result;
+ }
/// Returns a non-negative random integer that is less than the specified maximum.
/// The exclusive upper bound of the random number to be generated. must be greater than or equal to 0.
@@ -107,7 +121,9 @@ public virtual long NextInt64(long maxValue)
ThrowMaxValueMustBeNonNegative();
}
- return _impl.NextInt64(maxValue);
+ long result = _impl.NextInt64(maxValue);
+ AssertInRange(result, 0, maxValue);
+ return result;
}
/// Returns a random integer that is within a specified range.
@@ -125,16 +141,28 @@ public virtual long NextInt64(long minValue, long maxValue)
ThrowMinMaxValueSwapped();
}
- return _impl.NextInt64(minValue, maxValue);
+ long result = _impl.NextInt64(minValue, maxValue);
+ AssertInRange(result, minValue, maxValue);
+ return result;
}
/// Returns a random floating-point number that is greater than or equal to 0.0, and less than 1.0.
/// A single-precision floating point number that is greater than or equal to 0.0, and less than 1.0.
- public virtual float NextSingle() => _impl.NextSingle();
+ public virtual float NextSingle()
+ {
+ float result = _impl.NextSingle();
+ AssertInRange(result);
+ return result;
+ }
/// Returns a random floating-point number that is greater than or equal to 0.0, and less than 1.0.
/// A double-precision floating point number that is greater than or equal to 0.0, and less than 1.0.
- public virtual double NextDouble() => _impl.NextDouble();
+ public virtual double NextDouble()
+ {
+ double result = _impl.NextDouble();
+ AssertInRange(result);
+ return result;
+ }
/// Fills the elements of a specified array of bytes with random numbers.
/// The array to be filled with random numbers.
@@ -155,7 +183,12 @@ public virtual void NextBytes(byte[] buffer)
/// Returns a random floating-point number between 0.0 and 1.0.
/// A double-precision floating point number that is greater than or equal to 0.0, and less than 1.0.
- protected virtual double Sample() => _impl.Sample();
+ protected virtual double Sample()
+ {
+ double result = _impl.Sample();
+ AssertInRange(result);
+ return result;
+ }
private static void ThrowMaxValueMustBeNonNegative() =>
throw new ArgumentOutOfRangeException("maxValue", SR.Format(SR.ArgumentOutOfRange_NeedNonNegNum, "maxValue"));
@@ -163,6 +196,23 @@ public virtual void NextBytes(byte[] buffer)
private static void ThrowMinMaxValueSwapped() =>
throw new ArgumentOutOfRangeException("minValue", SR.Format(SR.Argument_MinMaxValue, "minValue", "maxValue"));
+ [Conditional("DEBUG")]
+ private static void AssertInRange(long result, long minInclusive, long maxExclusive)
+ {
+ if (maxExclusive > minInclusive)
+ {
+ Debug.Assert(result >= minInclusive && result < maxExclusive, $"Expected {minInclusive} <= {result} < {maxExclusive}");
+ }
+ else
+ {
+ Debug.Assert(result == minInclusive, $"Expected {minInclusive} == {result}");
+ }
+ }
+
+ [Conditional("DEBUG")]
+ private static void AssertInRange(double result) =>
+ Debug.Assert(result >= 0.0 && result < 1.0f, $"Expected 0.0 <= {result} < 1.0");
+
/// Random implementation that delegates all calls to a ThreadStatic Impl instance.
private sealed class ThreadSafeRandom : Random
{
@@ -189,7 +239,12 @@ private sealed class ThreadSafeRandom : Random
[MethodImpl(MethodImplOptions.NoInlining)]
private static XoshiroImpl Create() => t_random = new();
- public override int Next() => LocalRandom.Next();
+ public override int Next()
+ {
+ int result = LocalRandom.Next();
+ AssertInRange(result, 0, int.MaxValue);
+ return result;
+ }
public override int Next(int maxValue)
{
@@ -198,7 +253,9 @@ public override int Next(int maxValue)
ThrowMaxValueMustBeNonNegative();
}
- return LocalRandom.Next(maxValue);
+ int result = LocalRandom.Next(maxValue);
+ AssertInRange(result, 0, maxValue);
+ return result;
}
public override int Next(int minValue, int maxValue)
@@ -208,10 +265,17 @@ public override int Next(int minValue, int maxValue)
ThrowMinMaxValueSwapped();
}
- return LocalRandom.Next(minValue, maxValue);
+ int result = LocalRandom.Next(minValue, maxValue);
+ AssertInRange(result, minValue, maxValue);
+ return result;
}
- public override long NextInt64() => LocalRandom.NextInt64();
+ public override long NextInt64()
+ {
+ long result = LocalRandom.NextInt64();
+ AssertInRange(result, 0, long.MaxValue);
+ return result;
+ }
public override long NextInt64(long maxValue)
{
@@ -220,7 +284,9 @@ public override long NextInt64(long maxValue)
ThrowMaxValueMustBeNonNegative();
}
- return LocalRandom.NextInt64(maxValue);
+ long result = LocalRandom.NextInt64(maxValue);
+ AssertInRange(result, 0, maxValue);
+ return result;
}
public override long NextInt64(long minValue, long maxValue)
@@ -230,12 +296,24 @@ public override long NextInt64(long minValue, long maxValue)
ThrowMinMaxValueSwapped();
}
- return LocalRandom.NextInt64(minValue, maxValue);
+ long result = LocalRandom.NextInt64(minValue, maxValue);
+ AssertInRange(result, minValue, maxValue);
+ return result;
}
- public override float NextSingle() => LocalRandom.NextSingle();
+ public override float NextSingle()
+ {
+ float result = LocalRandom.NextSingle();
+ AssertInRange(result);
+ return result;
+ }
- public override double NextDouble() => LocalRandom.NextDouble();
+ public override double NextDouble()
+ {
+ double result = LocalRandom.NextDouble();
+ AssertInRange(result);
+ return result;
+ }
public override void NextBytes(byte[] buffer)
{
diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs
index 8a6491aef05308d37605254a661b195810c908bf..1cc1ccc21ec97ec6c161d69b764548ca33010e8a 100644
--- a/src/libraries/System.Runtime.Extensions/tests/System/Random.cs
+++ b/src/libraries/System.Runtime.Extensions/tests/System/Random.cs
@@ -130,6 +130,31 @@ public void Next_IntInt_AllValuesWithinSmallRangeHit(bool derived, bool seeded)
Assert.DoesNotContain(44, hs);
}
+ public static IEnumerable