69

Can somebody verify this method. I need a long type number inside a range of two longs. I use the .NET Random.Next(min, max) function which return int's. Is my reasoning correct if I simply divide the long by 2, generate the random number and finally multiply it by 2 again? Or am I too enthusiastic... I understand that my random resolution will decrease but are there any other mistakes which will lead to no such a random number.

long min = st.MinimumTime.Ticks;    //long is Signed 64-bit integer
long max = st.MaximumTime.Ticks;
int minInt = (int) (min / 2);      //int is Signed 64-bit integer
int maxInt = (int) (max / 2);      //int is Signed 64-bit integer

Random random = new Random();
int randomInt = random.Next(minInt, maxInt);
long randomLong = (randomInt * 2);
Nick V
  • 1,793
  • 2
  • 17
  • 19

18 Answers18

110

Why don't you just generate two random Int32 values and make one Int64 out of them?

long LongRandom(long min, long max, Random rand) {
    long result = rand.Next((Int32)(min >> 32), (Int32)(max >> 32));
    result = (result << 32);
    result = result | (long)rand.Next((Int32)min, (Int32)max);
    return result;
}

Sorry, I forgot to add boundaries the first time. Added min and max params. You can test it like that:

long r = LongRandom(100000000000000000, 100000000000000050, new Random());

Values of r will lie in the desired range.

EDIT: the implementation above is flawed. It's probably worth it to generate 4 16-bit integers rather than 2 32-bit ones to avoid signed-unsigned problems. But at this point the solution loses its elegancy, so I think it's best to stick with Random.NextBytes version:

long LongRandom(long min, long max, Random rand) {
    byte[] buf = new byte[8];
    rand.NextBytes(buf);
    long longRand = BitConverter.ToInt64(buf, 0);

    return (Math.Abs(longRand % (max - min)) + min);
}

It looks pretty well in terms of value distribution (judging by very simple tests I ran).

Dyppl
  • 12,161
  • 9
  • 47
  • 68
  • 2
    @user839032: it actually has bugs in it, the snippet above was meant to demonstrate and idea. I'll update the answer later with a robust solution – Dyppl Jul 12 '11 at 08:09
  • I implemented it and a first shot looks good. Can it be that the but is here "result = result | (long)rand.Next((Int32)min, (Int32)max);" where the int casts can become negative? – Nick V Jul 12 '11 at 08:20
  • @user839032: this is a naive implementation, it will fail at certain samples because Int32 is a signed type so it acts weird when you try to use it as bits of unsigned number. One solution is to make 4 random numbers 16 bytes each, not 2 like in my sample – Dyppl Jul 12 '11 at 08:23
  • @user893032: please check my corrected answer. It seems like it's best to stick with using `NextBytes` after all, I included the working sample. – Dyppl Jul 12 '11 at 08:40
  • 15
    Doesn't work properly. `LongRandom(long.MinValue, long.MaxValue, new Random())`, always returns `-9223372036854775808`. In any case, `Math.Abs()` destroys one bit, leaving you with 63 random bits. You can't provide a 64-bit random number if you only have 63 random bits. – Allon Guralnek Mar 15 '12 at 14:48
  • 1
    The will only work if min and max for some powers of two :] e.g. min=0 and max=0x100000000 will only produce those two values. The second will work if max - min doesn't overflow to be a negative number. – Peter Lawrey Dec 28 '12 at 18:27
  • As `Random` works on 32bits even if you generate 8 bytes, there can only be 2^32 possible outcomes. So you will only generate 2^32 `long`s – weston Dec 10 '14 at 10:33
  • Worth mentioning that in your updated version, the `max` is exclusive max, consistent with the `System.Random.Next()` method, which is good. – KFL Mar 11 '15 at 00:13
  • @Dyppl are you `SURE` that it will be uniformly generated value? – Alex Zhukovskiy Feb 05 '16 at 14:40
  • 1
    @weston, that's not correct. -- Random can fill an 8 byte array just fine. – BrainSlugs83 Mar 21 '18 at 00:36
  • @BrainSlugs83 I think my point was, how many seeds can you start random with? `2^32`. So how many different 8 byte sequences can a new instance of Random produce? No more than `2^32`. So have you made a random 64bit? Not really if the vast majority of 64bit numbers can't be produced in the first draw. – weston Mar 21 '18 at 00:44
  • @weston That's a good point. You may want to "double seed" the generator by using a random seed, then asking for (and discarding) a random number of bytes. – Daniel Nov 13 '18 at 15:28
  • @Daniel but where does that second random number come from? – weston Nov 14 '18 at 19:45
  • @weston I recommend getting it from `System.Security.Cryptography.RNGCryptoServiceProvider` – Daniel Nov 16 '18 at 05:04
  • @Daniel then you might as well get the long from there. – weston Nov 16 '18 at 05:11
  • @weston that's correct, if you only need one of them. – Daniel Nov 17 '18 at 09:06
  • @Daniel I don't know why needing 1 is important. It all depends on what you need it for. If you need a repeatable random long, you can't use a random that can't be seeded. If it's for security or fair gaming, then you would only use a secure random. I do not think you ever want to mix them in one operation. – weston Nov 17 '18 at 12:24
  • @weston it's a performance issue. Secure crypto is much slower, but you can use it to provide a seed that isn't limited to 2^32 random value streams. Then subsequent random values can come from a more performant library. – Daniel Nov 19 '18 at 00:34
  • @Daniel I think you've not thought it through fully. What would skipping a random number of values look like? How many is enough? Too low and you've not added enough extra entropy and too high you're going to cause performance issues yourself. – weston Nov 19 '18 at 00:38
  • @Daniel Let's say you skip between `[0..1024)` random numbers at the start. Now I accept that first 64 bit random is from a larger set, but this set has just grown from `2^32` to `1024*2^32 = 2^42`. It's still not a fair draw in the range of `2^64`. To do that we need to skip `[0..2^32)` random numbers in a loop! That is clearly not practical. – weston Nov 19 '18 at 00:55
  • @weston That's correct, you need to skip (up to) 32 bits to provide 64 bits. It takes about 8 seconds in the worst case scenario on my PC--acceptable in use cases where some start-up time is acceptable. If something more performant is needed, I'd switch over to SFMT (a hyper-optimized Mersenne Twister). – Daniel Nov 20 '18 at 06:11
  • @Daniel 8 seconds is acceptable?!! Yet using a secure random is a perf issue? Face facts, this method is simply not a good idea. – weston Nov 20 '18 at 07:40
  • @weston I can only speak for my uses. My application uses trillions of new random numbers every run. That isn't that unusual. Bear in mind that this is only if you need 2^64 starting positions. If you only need 2^61, it will only take 1 second. If you only need 2^57, it will take less than a tenth of a second. – Daniel Nov 21 '18 at 14:34
  • Concatenating integers won't really generate a random number because it guarantees that the "random" number generated will never be smaller than the largest possible integer value, plus one. A better method would be to create two random 32-bit integers and multiply them together to get your 64-bit `long` value. – Jim Fell Feb 16 '19 at 03:51
  • 1
    Need to test your edge cases. If `min == long.MinValue` and `max == long.MaxValue`, it always returns the same number. – Jonathan Wood Jul 20 '19 at 00:47
  • This doesn't work. Ran this in a loop for 10 minutes and wasn't able to produce a value less than int.MaxValue – Phill Nov 22 '21 at 08:03
  • C#10 now has long randoms built in. Use NextInt64 if you can. https://learn.microsoft.com/en-us/dotnet/api/system.random.nextint64?view=net-6.0 – Guy Oct 14 '22 at 05:45
51

Some other answers here have two issues: having a modulo bias, and failing to correctly handle values of max = long.MaxValue. (Martin's answer has neither problem, but his code is unreasonably slow with large ranges.)

The following code will fix all of those issues:

//Working with ulong so that modulo works correctly with values > long.MaxValue
ulong uRange = (ulong)(max - min);

//Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
//for more information.
//In the worst case, the expected number of calls is 2 (though usually it's
//much closer to 1) so this loop doesn't really hurt performance at all.
ulong ulongRand;
do
{
    byte[] buf = new byte[8];
    random.NextBytes(buf);
    ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
} while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);

return (long)(ulongRand % uRange) + min;

The following fully-documented class can be dropped into your codebase to implement the above solution easily and brain-free. Like all code on Stackoverflow, it's licensed under CC-attribution, so you can feel free to use to use it for basically whatever you want.

using System;

namespace MyNamespace
{
    public static class RandomExtensionMethods
    {
        /// <summary>
        /// Returns a random long from min (inclusive) to max (exclusive)
        /// </summary>
        /// <param name="random">The given random instance</param>
        /// <param name="min">The inclusive minimum bound</param>
        /// <param name="max">The exclusive maximum bound.  Must be greater than min</param>
        public static long NextLong(this Random random, long min, long max)
        {
            if (max <= min)
                throw new ArgumentOutOfRangeException("max", "max must be > min!");

            //Working with ulong so that modulo works correctly with values > long.MaxValue
            ulong uRange = (ulong)(max - min);

            //Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
            //for more information.
            //In the worst case, the expected number of calls is 2 (though usually it's
            //much closer to 1) so this loop doesn't really hurt performance at all.
            ulong ulongRand;
            do
            {
                byte[] buf = new byte[8];
                random.NextBytes(buf);
                ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
            } while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);

            return (long)(ulongRand % uRange) + min;
        }

        /// <summary>
        /// Returns a random long from 0 (inclusive) to max (exclusive)
        /// </summary>
        /// <param name="random">The given random instance</param>
        /// <param name="max">The exclusive maximum bound.  Must be greater than 0</param>
        public static long NextLong(this Random random, long max)
        {
            return random.NextLong(0, max);
        }

        /// <summary>
        /// Returns a random long over all possible values of long (except long.MaxValue, similar to
        /// random.Next())
        /// </summary>
        /// <param name="random">The given random instance</param>
        public static long NextLong(this Random random)
        {
            return random.NextLong(long.MinValue, long.MaxValue);
        }
    }
}

Usage:

Random random = new Random();
long foobar = random.NextLong(0, 1234567890L);
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • @agent-j: Good catch on `max == min`, I'll fix that now *(it actually should **not** work, since `max` is an exclusive upper-bound; but it was not throwing an exception like it should)*. However, the bias *is* already fixed; `ulongRand` is evenly distributed over `0` to `uRange * (int)(ulong.MaxValue / uRange) - 1`, meaning `ulongRange % uRange` is evenly distributed over `0` to `uRange - 1` – BlueRaja - Danny Pflughoeft Oct 26 '12 at 23:48
  • There still seems to be a bias for small ranges (though one could argue it's not that very big). The value of `ulong.MaxValue` is `18,446,744,073,709,551,615` (notice that it ends in `615`). If we pick a uRange of size `100`, and `ulongRand` falls the last `15` `(ulong.MaxValue % uRange)`, it should be thrown out, but you are throwing out the last 100 (by subtracting `uRange`), thus giving a tiny bias. Perhaps this is what you need `while (ulongRand >= ulong.MaxValue - (ulong.MaxValue % uRange))` – agent-j Oct 29 '12 at 16:50
  • @agent-j: You're right, but that correction still doesn't work quite right *(try `ulong.MaxValue = 9, uRange = 5` - every value from 0-9 should be valid, but your solution loops for half the space!)*. You need to add 1 to `ulong.MaxValue` for it to be correct.. but that *still* won't work because the value will loop around! So, we need to mod before adding, then mod again *(in case we hit `uRange`)*. The final correction looks like this: `while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange)` – BlueRaja - Danny Pflughoeft Nov 06 '12 at 21:58
  • 2
    My timings show that my code takes ~210ms for generating 1,000,000 random numbers between `long.MinValue` and `long.MaxValue` and takes yours ~260ms for the same range and number of iterations. Pretty quick either way, I'm just defending that my code is not "unreasonably slow with large ranges" – Marty Neal Dec 03 '12 at 23:02
  • 1
    Your `NextLong` should map to `NextLong(0, long.MaxValue)` because `Random.Next()` returns a non-negative integer. Also, perhaps initialize `buf` outside the loop for the rare times it loops? – NetMage May 07 '19 at 22:21
  • I guess using random bits to get a unbiased random value that is not in a range of a power of 2, for example a random number from (0, 1, 2) is impossible, as 2^n % 3 is never 0 for any number of bits n. – Wouter Jul 28 '21 at 19:16
  • What do you think of Dyppl's answer at the top? Is that unbiased? Also, I suggest scrapping the other two overloaded methods and simply declaring the main one like this: `public long NextLong(long min=0, long max = long.MaxValue)`. As well as being flexible, this also imitates C#'s usage of `Next` much more closely (starts at zero). – Dan W Apr 09 '22 at 23:47
28

This creates a random Int64 by using random bytes, avoiding modulo bias by retrying if the number is outside the safe range.

static class RandomExtensions
{
   public static long RandomLong(this Random rnd)
   {
      byte[] buffer = new byte[8];
      rnd.NextBytes (buffer);
      return BitConverter.ToInt64(buffer, 0);
   }

   public static long RandomLong(this Random rnd, long min, long max)
   {
      EnsureMinLEQMax(ref min, ref max);
      long numbersInRange = unchecked(max - min + 1);
      if (numbersInRange < 0)
         throw new ArgumentException("Size of range between min and max must be less than or equal to Int64.MaxValue");

      long randomOffset = RandomLong(rnd);
      if (IsModuloBiased(randomOffset, numbersInRange))
         return RandomLong(rnd, min, max); // Try again
      else
         return min + PositiveModuloOrZero(randomOffset, numbersInRange);
   }

   static bool IsModuloBiased(long randomOffset, long numbersInRange)
   {
      long greatestCompleteRange = numbersInRange * (long.MaxValue / numbersInRange);
      return randomOffset > greatestCompleteRange;
   }

   static long PositiveModuloOrZero(long dividend, long divisor)
   {
      long mod;
      Math.DivRem(dividend, divisor, out mod);
      if(mod < 0)
         mod += divisor;
      return mod;
   }

   static void EnsureMinLEQMax(ref long min, ref long max)
   {
      if(min <= max)
         return;
      long temp = min;
      min = max;
      max = temp;
   }
}
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
agent-j
  • 27,335
  • 5
  • 52
  • 79
  • 8
    You should really have named your extension methods `NextLong` instead of `RandomLong`, as they would follow the naming convention of the existing methods of the `Random` class. – BrainSlugs83 Mar 21 '18 at 00:44
10

Here is a solution that leverages from the other answers using Random.NextBytes, but also pays careful attention to boundary cases. I've structured it as a set of extension methods. Also, I've accounted for modulo bias, by sampling another random number it falls out of range.

One of my gripes (at least for the situation I was trying to use it) is that the maximum is usually exclusive so if you want to roll a die, you do something like Random.Next(0,7). However, this means you can never get this overload to return the .MaxValue for the datatype (int, long, ulong, what-have-you). Therefore, I've added an inclusiveUpperBound flag to toggle this behavior.

public static class Extensions
{
    //returns a uniformly random ulong between ulong.Min inclusive and ulong.Max inclusive
    public static ulong NextULong(this Random rng)
    {
        byte[] buf = new byte[8];
        rng.NextBytes(buf);
        return BitConverter.ToUInt64(buf, 0);
    }

    //returns a uniformly random ulong between ulong.Min and Max without modulo bias
    public static ulong NextULong(this Random rng, ulong max, bool inclusiveUpperBound = false)
    {
        return rng.NextULong(ulong.MinValue, max, inclusiveUpperBound);
    }

    //returns a uniformly random ulong between Min and Max without modulo bias
    public static ulong NextULong(this Random rng, ulong min, ulong max, bool inclusiveUpperBound = false)
    {
        ulong range = max - min;

        if (inclusiveUpperBound)
        {   
            if (range == ulong.MaxValue)
            {
                return rng.NextULong();
            }

            range++;
        }

        if (range <= 0)
        {
            throw new ArgumentOutOfRangeException("Max must be greater than min when inclusiveUpperBound is false, and greater than or equal to when true", "max");
        }

        ulong limit = ulong.MaxValue - ulong.MaxValue % range;
        ulong r;
        do
        {
            r = rng.NextULong();
        } while(r > limit);

        return r % range + min;
    }

    //returns a uniformly random long between long.Min inclusive and long.Max inclusive
    public static long NextLong(this Random rng)
    {
        byte[] buf = new byte[8];
        rng.NextBytes(buf);
        return BitConverter.ToInt64(buf, 0);
    }

    //returns a uniformly random long between long.Min and Max without modulo bias
    public static long NextLong(this Random rng, long max, bool inclusiveUpperBound = false)
    {
        return rng.NextLong(long.MinValue, max, inclusiveUpperBound);
    }

    //returns a uniformly random long between Min and Max without modulo bias
    public static long NextLong(this Random rng, long min, long max, bool inclusiveUpperBound = false)
    {
        ulong range = (ulong)(max - min);

        if (inclusiveUpperBound)
        {   
            if (range == ulong.MaxValue)
            {
                return rng.NextLong();
            }

            range++;
        }

        if (range <= 0)
        {
            throw new ArgumentOutOfRangeException("Max must be greater than min when inclusiveUpperBound is false, and greater than or equal to when true", "max");
        }

        ulong limit = ulong.MaxValue - ulong.MaxValue % range;
        ulong r;
        do
        {
            r = rng.NextULong();
        } while(r > limit);
        return (long)(r % range + (ulong)min);
    }
}
Marty Neal
  • 8,741
  • 3
  • 33
  • 38
5
private long randomLong()
{
    Random random = new Random();
    byte[] bytes = new byte[8];
    random.NextBytes(bytes);
    return BitConverter.ToInt64(bytes, 0);
}
Torben Kohlmeier
  • 6,713
  • 1
  • 15
  • 15
Vincent
  • 842
  • 7
  • 13
  • Nice, I wasn't interested in a range of values, just wanted a simple way to get some random 64 bits. Thank you. – okovko Apr 08 '21 at 16:16
4

This will get you a secure random long:

using (RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider()) 
{ 
    byte[] rno = new byte[9];    
    rg.GetBytes(rno);    
    long randomvalue = BitConverter.ToInt64(rno, 0); 
}
Rob123
  • 895
  • 6
  • 10
4

Start at the minimum, add a random percentage of the difference between the min and the max. Problem with this is that NextDouble returns a number x such that 0 <= x < 1, so there's a chance you'll never hit the max number.

long randomLong = min + (long)(random.NextDouble() * (max - min));
Coeffect
  • 8,772
  • 2
  • 27
  • 42
  • Based on the number of digits in double's precision, this would exclude a lot of numbers, but still a good method. – Yuriy Faktorovich Jul 11 '11 at 14:34
  • I can see that happening if min and max are sufficiently far apart, in which case you'd have to test it millions of times before any problem became apparent. – Coeffect Jul 11 '11 at 14:44
2

You can try CryptoRandom of the Inferno library:

public class CryptoRandom : Random
    // implements all Random methods, as well as:

    public byte[] NextBytes(int count)
    public long NextLong()
    public long NextLong(long maxValue)
    public long NextLong(long minValue, long maxValue)
VahidN
  • 18,457
  • 8
  • 73
  • 117
2

I wrote some Test Methods and check my own method and many of the answers from this and the same questions. Generation of redundant values is a big problem. I found @BlueRaja - Danny Pflughoeft answer at this address Is good enough and did not generate redundant values at least for first 10,000,000s. This is a Test Method:

[TestMethod]
    public void TestRand64WithExtensions()
    {
        Int64 rnum = 0;
        HashSet<Int64> hs = new HashSet<long>();
        Random randAgent = new Random((int)DateTime.Now.Ticks);

        for (int i = 0; i < 10000000; i++)
        {
            rnum = randAgent.NextLong(100000000000000, 999999999999999);
            //Test returned value is greater than zero
            Assert.AreNotEqual(0, rnum);
            //Test Length of returned value
            Assert.AreEqual(15, rnum.ToString().Length);
            //Test redundancy
            if (!hs.Contains(rnum)) { hs.Add(rnum); }
            else
            {
                //log redundant value and current length we received
                Console.Write(rnum + " | " + hs.Count.ToString());
                Assert.Fail();
            }
        }
    }

I didn't want to post this as an answer but I can't stuff this in the comment section and I didn't want to add as an edit to answer without author consent. So pardon me as this is not an independent answer and maybe just a prove to one of the answers.

QMaster
  • 3,743
  • 3
  • 43
  • 56
  • Great 'answer'. I think Stackoverflow needs to consider 'meta' answers like this as an addition to normal answers. – Dan W Apr 09 '22 at 23:50
  • 1
    @DanW Thanks for your kindly response and bringing up an important point. As I said, I was unable to add the source in the comment section, so I had to provide it in the discrete answer. In terms of adding the ability to a meta answer, I agree with you, and I would be glad to accompany you if you decide to make an official suggestion. – QMaster Apr 14 '22 at 21:29
2

Your randomLong will always be even and you will have eliminated even more values because you are very far away from the maximum for long, The maximum for long is 2^32 * max for int. You should use Random.NextBytes.

Yuriy Faktorovich
  • 67,283
  • 14
  • 105
  • 142
2

C#10 now has long randoms built in.

Random random = new Random();
long myRandomNumber = random.NextInt64(min, max);
SunsetQuest
  • 8,041
  • 2
  • 47
  • 42
Guy
  • 1,232
  • 10
  • 21
1

I wrote a benchmarking C# console app that tests 5 different methods for generating unsigned 64-bit integers. Some of those methods are mentioned above. Method #5 appeared to consistently be the quickest. I claim to be no coding genius, but if this helps you, you're welcome to it. If you have better ideas, please submit. - Dave (sbda26@gmail.com)

enter code here

  static private Random _clsRandom = new Random();
  private const int _ciIterations = 100000;

  static void Main(string[] args)
  {
      RunMethod(Method1);
      RunMethod(Method2);
      RunMethod(Method3);
      RunMethod(Method4);
      RunMethod(Method5);

      Console.ReadLine();
  }

  static void RunMethod(Func<ulong> MethodX)
  {
      ulong ulResult;
      DateTime dtStart;
      TimeSpan ts;

      Console.WriteLine("--------------------------------------------");
      Console.WriteLine(MethodX.Method.Name);
      dtStart = DateTime.Now;
      for (int x = 1; x <= _ciIterations; x++)
          ulResult = MethodX.Invoke();
      ts = DateTime.Now - dtStart;

      Console.WriteLine(string.Format("Elapsed time: {0} milliseconds", ts.TotalMilliseconds));
  }

  static ulong Method1()
  {
      int x1 = _clsRandom.Next(int.MinValue, int.MaxValue);
      int x2 = _clsRandom.Next(int.MinValue, int.MaxValue);
      ulong y;

      // lines must be separated or result won't go past 2^32
      y = (uint)x1;
      y = y << 32;
      y = y | (uint)x2;

      return y;
  }

  static ulong Method2()
  {
      ulong ulResult = 0;

      for(int iPower = 0; iPower < 64; iPower++)
      {
          double dRandom = _clsRandom.NextDouble();
          if(dRandom > 0.5)
          {
              double dValue = Math.Pow(2, iPower);
              ulong ulValue = Convert.ToUInt64(dValue);
              ulResult = ulResult | ulValue;
          }
      }

      return ulResult;
  }

  static ulong Method3()  // only difference between #3 and #2 is that this one (#3) uses .Next() instead of .NextDouble()
  {
      ulong ulResult = 0;

      for (int iPower = 0; iPower < 64; iPower++)
          if (_clsRandom.Next(0, 1) == 1)
              ulResult = ulResult | Convert.ToUInt64(Math.Pow(2, iPower));

      return ulResult;
  }

static ulong Method4()
{
    byte[] arr_bt = new byte[8];
    ulong ulResult;

    _clsRandom.NextBytes(arr_bt);
    ulResult = BitConverter.ToUInt64(arr_bt, 0);
    return ulResult;
}

// Next method courtesy of https://stackoverflow.com/questions/14708778/how-to-convert-unsigned-integer-to-signed-integer-without-overflowexception/39107847
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)]
struct EvilUnion
{
    [System.Runtime.InteropServices.FieldOffset(0)] public int Int32;
    [System.Runtime.InteropServices.FieldOffset(0)] public uint UInt32;
}
static ulong Method5()
{
    var evil = new EvilUnion();
    ulong ulResult = 0;

    evil.Int32 = _clsRandom.Next(int.MinValue, int.MaxValue);
    ulResult = evil.UInt32;
    ulResult = ulResult << 32;
    evil.Int32 = _clsRandom.Next(int.MinValue, int.MaxValue);
    ulResult = ulResult | evil.UInt32;

    return ulResult;
}

}

1

I'll add my solution for generating random unsigned long integer (random ulong) below max value.

    public static ulong GetRandomUlong(ulong maxValue)
    {
        Random rnd = new Random();
        
        //This algorithm works with inclusive upper bound, but random generators traditionally have exclusive upper bound, so we adjust.
        //Zero is allowed, function will return zero, as well as for 1. Same behavior as System.Random.Next().
        if (maxValue > 0) maxValue--;   
                    
        byte[] maxValueBytes = BitConverter.GetBytes(maxValue); 
        byte[] result = new byte[8];
                    
        int i;
        for(i = 7; i >= 0; i--)
        {
                //senior bytes are either zero (then Random will write in zero without our help), or equal or below that of maxValue
                result[i] = (byte)rnd.Next( maxValueBytes[i] + 1 );         
                
                //If, going high bytes to low bytes, we got ourselves a byte, that is lower than that of MaxValue, then lower bytes may be of any value.
                if ((uint)result[i] < maxValueBytes[i])  break;
        }
                        
        for(i--; i >= 0; i--) // I like this row
        {
                result[i] = (byte)rnd.Next(256);
        }
                
        return BitConverter.ToUInt64(result, 0);
    }
Pavel Masyuk
  • 123
  • 1
  • 1
  • 8
-1

Is there anything wrong with using this simple approach?

        long min = 10000000000001;
        long max = 99999999999999;
        Random random = new Random();
        long randomNumber = min + random.Next() % (max - min);

d

-1

My worked solution. Tested for 1000+ times:

public static long RandomLong(long min, long max)
{
   return min + (long)RandomULong(0, (ulong)Math.Abs(max - min));
}
public static ulong RandomULong(ulong min, ulong max)
{
   var hight = Rand.Next((int)(min >> 32), (int)(max >> 32));
   var minLow = Math.Min((int)min, (int)max);
   var maxLow = Math.Max((int)min, (int)max);
   var low = (uint)Rand.Next(minLow, maxLow);
   ulong result = (ulong)hight;
   result <<= 32;
   result |= (ulong)low;
   return result;
}
SunleoSun
  • 61
  • 1
  • 1
-1

What's wrong with generating a double to be intended as a factor to be used to calculate the actual long value starting from the max value a long can be?!

long result = (long)Math.Round( random.NextDouble() * maxLongValue );
  • NextDouble generates a random number between [0.0, 0.99999999999999978] (msdn doc)

  • You multiply this random number by your maxLongValue.

  • You Math.Round that result so you can get the chance to get maxLongValue anyway (eg: simulate you got 1.0 from the NextDouble).

  • You cast back to long.
Mauro Sampietro
  • 2,739
  • 1
  • 24
  • 50
  • The problem here is that Random.NextDouble operates in a 32-bit range, so your sample range is way smaller than you'd think. I was using this initially and noticed that I got a horrible amount of collisions. – Philipp Sumi Apr 21 '17 at 15:09
  • @PhilippSumi 32 bit range is ok as long as generating integers is ok. You should have to prove you get more collisions than in theory to us in order to check you are missing something else. – Mauro Sampietro Apr 23 '17 at 09:13
  • This thread and the solution was posted in the context of long numbers, which are 64 bits. With a 32-bit range, your risk of getting collisions is nearly 100% with only 100K samples. I actually got bit by this just recently. For a probability graph, see also https://blogs.msdn.microsoft.com/ericlippert/2010/03/22/socks-birthdays-and-hash-collisions/ – Philipp Sumi Apr 23 '17 at 21:51
  • @PhilippSumi since i'm using the random number as a percentage and i calculate the long number by multiplying that percentage with the max value a long number can be, this solution is all right. – Mauro Sampietro Aug 29 '17 at 08:17
  • When maxLongValue == long.MaxValue this shows that the resolution is limited and this will only generate even numbers. – Wouter Jul 28 '21 at 18:49
-1

How about generating bytes and converting to int64?

/* generate a byte array, then convert to unint64 */
var r = new Random(); // DONT do this for each call - use a static Random somewhere
var barray = new byte[64/8];
r.NextBytes(barray);
var rint64 = BitConverter.ToUInt64(barray, 0);

Sees to work for me (:

knut
  • 689
  • 5
  • 13
-1

You're better off taking the difference between minimum and maximum (if it fits in an int), getting a random between 0 and that, and adding it to the minimum.

antlersoft
  • 14,636
  • 4
  • 35
  • 55