Proposed fix to random()

In its current implementation, random() as defined in WMath.cpp takes longs as arguments and returns a long, but it relies upon rand() from stdlib.h, which returns an int. As a result, random(howbig) will never return a number greater than 0x7FFF, and random(howsmall, howbig) can return strange and unexpected results when its arguments are longs.

I propose the following fix to random(howbig):

long random(long howbig)
{
  if (howbig == 0){
    return 0;
  }
  return (rand()*0x10000L + rand()) % howbig;
}
  • Ben

Sounds good to me. I just changed the code; I'll test it and check it in shortly.

Thanks for taking the time to find a solution.

thanks man it works a treat! ;D

I'm still seeing odd behavior -- should the docs be updated to reflect that it doesn't return a long and that it can't return higher than 64K?

// returns a number between 1, 32K or so. I haven't verified that it // returns as high as 32768, but I'll assume it does long i = random(1, 32768);

// returns a number between -32k and 32k long i = random(1, 65536);

// returns a number between 1 and 64K unsigned long i = random(1, 65536);

// returns 1 long i = random(1, 131072);

jet–

Are you using Arduino 0012? I’m guessing not, since your results are quite different from mine.

That said, the new version of random() is still flawed, as the following sketch demonstrates:

void setup()
{
Serial.begin(9600);
}
long mx = 0;
void loop()
{
long r = random(0, 0x30000); // generate numbers between 0 and 0x30000
if (r > mx) // and record the maximum number seen
{
Serial.print("New max: ");
Serial.println(r, HEX);
mx = r;
}
}

No matter how long you run this, you will never get a random number anywhere near 0x30000 as you can see by this output of a 2-minute run:

New max: 13AF1
New max: 25AC8
New max: 25BE2
New max: 278FE
New max: 27C1E
New max: 27E23
New max: 27F2E
New max: 27FF3
New max: 27FFF

The reason for this is that the expression, found in the random() source,

rand() * 0x10000L + rand()

maxes out at 0x7FFF7FFF and can never have bit 15 set. This is potentially serious, as this means that given certain N, approximately half of the theoretically possible return values for random(N) can in fact never be reached. For example, the expression

if (random(65536) == 33059)

will always fail with 0012.

One possible fix is:

long random(long howbig)
{
  if (howbig == 0){
    return 0;
  }
  return (((long)rand() << 15) | rand()) % howbig;
}

This code returns every possible value within its range. One might object that the maximum return is reduced to 0x3FFFFFFF, but following the example of C runtimes everywhere, I would propose simply documenting this and adding something like:

#define RANDOM_MAX 0x3FFFFFFF

Alternatively, one could concoct an expression using three rand() calls concatenated together, but I wouldn’t like to sacrifice the efficiency.

Mikal

Yes, using Arduino 12 on OSX.

I'm curious to understand how you got your results, jet. Would you mind posting or PM'ing the sketch that generated them?

Mikal

Ooh! Here’s the best fix of all:

long random(long howbig)
{
  if (howbig == 0){
    return 0;
  }
  [glow]return random() % howbig;[/glow]
}

I’ll leave it as an exercise to the reader to figure out how that works. No I won’t. :slight_smile: It calls the stdlib.h implementation of random()! This is much preferred, because not only is it (probably) more robust, it covers the full range 0 to 0x7FFFFFFF and is nearly twice as fast as our homespun implementation.

David, if you decide to adopt this for 0013, we’ll also have to change randomSeed accordingly:

void randomSeed(unsigned int seed)
{
  if(seed != 0){
    srandom(seed);
  }
}

Mikal

cool, thanks! I'm happy to test it in my workbranch.