I am migrating a method that is used for decoding from .NET Framework 1.1 to .NET Framework 4. I noticed that implementation of Random changed. So given the same seed, Random.NextBytes returns different result.
So if I run the following code.
byte[] bytes = new byte[4];
System.Random random = new System.Random(50);
random.NextBytes(bytes);
for(int i=0; i< bytes.Length; i++)
{
Console.WriteLine("bytes[" + i + "] = " + bytes[i]);
}
Under .NET Framework 1.1 it returns:
bytes[0] = 216
bytes[1] = 124
bytes[2] = 183
bytes[3] = 58
Under .NET framework 4 it returns:
bytes[0] = 154
bytes[1] = 49
bytes[2] = 183
bytes[3] = 48
What is the best way to resolve this problem?
The implementation of the random number generator in the Random class isn't guaranteed to remain the same across major versions of the .NET Framework. As a result, you shouldn't assume that the same seed will result in the same pseudo-random sequence in different versions of the .NET Framework.
The current implementation of the Random class is based on a modified version of Donald E. Knuth's subtractive random number generator algorithm. For more information, see D. E. Knuth. "The Art of Computer Programming, volume 2: Seminumerical Algorithms". Addison-Wesley, Reading, MA, second edition, 1981. All calculations are based on seed.
The results are interesting: initialization of Random class instance on .NET Framework takes about three times faster than on .NET Core. It implies that reading Environment.TickCount is still faster than using random generators to get the seed. Both platforms, .NET Framework and .NET Core, have different ways to implement Random class.
With the introduction of .NET Core 2.0, the internal implementation of the System.Random class was revised slightly compared to the legacy .NET Framework. From a usage perspective, things remained virtually unchanged - but there are some minor nuances that may help to be aware of.
This is not a problem with Random
, it satisfies its documented interface perfectly fine. This is a problem with your software relying on an implementation detail. Learn from this mistake and don't do it again.
As far as fixing the problem, you can implement your own version of 1.1's pseudorandom number generation for decoding and then implement a new encoding/decoding algorithm that doesn't rely on unstable behavior (such as the implementation of Random
or GetHashCode
) for your new version of the software.
You can just use Reflector to copy the Random class from the 1.1 mscorlib.
public class Random1_1
{
// Fields
private int inext;
private int inextp;
private const int MBIG = 0x7fffffff;
private const int MSEED = 0x9a4ec86;
private const int MZ = 0x0;
private int[] SeedArray;
// Methods
public Random1_1()
: this(Environment.TickCount)
{
}
public Random1_1(int Seed)
{
this.SeedArray = new int[0x38];
int num2 = 0x9a4ec86 - Math.Abs(Seed);
this.SeedArray[0x37] = num2;
int num3 = 0x1;
for (int i = 0x1; i < 0x37; i++)
{
int index = (0x15 * i) % 0x37;
this.SeedArray[index] = num3;
num3 = num2 - num3;
if (num3 < 0x0)
{
num3 += 0x7fffffff;
}
num2 = this.SeedArray[index];
}
for (int j = 0x1; j < 0x5; j++)
{
for (int k = 0x1; k < 0x38; k++)
{
this.SeedArray[k] -= this.SeedArray[0x1 + ((k + 0x1e) % 0x37)];
if (this.SeedArray[k] < 0x0)
{
this.SeedArray[k] += 0x7fffffff;
}
}
}
this.inext = 0x0;
this.inextp = 0x15;
Seed = 0x1;
}
public virtual int Next()
{
return (int)(this.Sample() * 2147483647.0);
}
public virtual int Next(int maxValue)
{
if (maxValue < 0x0)
{
throw new ArgumentOutOfRangeException("maxValue");
}
return (int)(this.Sample() * maxValue);
}
public virtual int Next(int minValue, int maxValue)
{
if (minValue > maxValue)
{
throw new ArgumentOutOfRangeException("minValue");
}
int num = maxValue - minValue;
if (num < 0x0)
{
long num2 = maxValue - minValue;
return (((int)((long)(this.Sample() * num2))) + minValue);
}
return (((int)(this.Sample() * num)) + minValue);
}
public virtual void NextBytes(byte[] buffer)
{
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
for (int i = 0x0; i < buffer.Length; i++)
{
buffer[i] = (byte)(this.Sample() * 256.0);
}
}
public virtual double NextDouble()
{
return this.Sample();
}
protected virtual double Sample()
{
int inext = this.inext;
int inextp = this.inextp;
if (++inext >= 0x38)
{
inext = 0x1;
}
if (++inextp >= 0x38)
{
inextp = 0x1;
}
int num = this.SeedArray[inext] - this.SeedArray[inextp];
if (num < 0x0)
{
num += 0x7fffffff;
}
this.SeedArray[inext] = num;
this.inext = inext;
this.inextp = inextp;
return (num * 4.6566128752457969E-10);
}
}
Tested and it gives the desired output.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With