I looked at the Rhino source code to find out which pseudo-random function they use. Apparently they Math.random to the Math.random function defined in the standard Java library .
The documentation for Math.random states:
Returns a double value with a positive sign greater than or equal to 0.0 and less than 1.0. Returned values ββare selected pseudo-randomly with (approximately) uniform distribution from this range.
When this method is first called, it creates one new pseudo-random number generator, just as if the expression
new java.util.Random
This new pseudo-random number generator is used for all calls to this method and is not used anywhere else.
This method is correctly synchronized to ensure proper use by multiple threads. However, if many streams need to generate pseudo-random numbers at high speed, this can reduce competition for each stream to have its own pseudo-random number generator.
So, I checked the documentation for java.util.Random and found this (for the default constructor):
Creates a new random number generator. Its seed is initialized with a value based on the current time:
public Random() { this(System.currentTimeMillis()); }
Two random objects created within one millisecond will have the same sequence of random numbers.
So, now we know for sure that the seed is the current time in milliseconds. In addition, the documentation for the second constructor says:
Creates a new random number generator using one long seed:
public Random(long seed) { setSeed(seed); }
Used by the method next to the state of the pseudo random number generator.
The documentation for the setSeed method says:
Sets the seed of this random number generator using one long seed. The general setSeed contract is that it changes the state of this random number generator object to be in exactly the same state as if it had just been created with the argument seed as the seed. The setSeed method is implemented by the Random class as follows:
synchronized public void setSeed(long seed) { this.seed = (seed ^ 0x5DEECE66DL) & ((1L << 48) - 1); haveNextNextGaussian = false; }
The random setSeed implementation uses only 48 bits of a given seed. In general, however, an overriding method can use all 64 bits of a long argument as an initial value. Note. Although the initial value is AtomicLong, this method must still be synchronized to ensure proper hasNextNextGaussian semantics.
actual method used to generate the nextDouble random number:
Returns the next pseudorandom uniformly distributed double value between 0.0 and 1.0 from this sequence of random number generators.
The implementation of the nextDouble function is as follows:
public double nextDouble() { return (((long)next(26) << 27) + next(27)) / (double)(1L << 53); }
Obviously, it depends on the next function:
Creates the following pseudo-random number. The subclass should override this as it is used by all other methods.
The implementation of the next function is as follows:
synchronized protected int next(int bits) { seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1); return (int)(seed >>> (48 - bits)); }
This is the pseudo-random function you are looking for. As the documentation says:
This is a linear congruent pseudo-random number generator, as defined by D. Kh. Lemer and described by Donald E. Knut in βThe Art of Programming,β Volume 2: βSeven-Dimensional Algorithms,β section 3.2.1.
Please note that this is only a random number generator used by Rhino. Other implementations, such as Spidermonkey and V8, may have their own pseudo random number generators.