The java.util.Random
class has a method nextLong() which on it's own calls next(32) returning a random signed integer.
public long nextLong() {
// it's okay that the bottom word remains signed.
return ((long)(next(32)) << 32) + next(32);
}
How come keeping the bottom word signed does not impact the quality of the randomly generated numbers? When constructing an example a bit in the middle of the generated long value is zeroed if the bottom word is negative.
final long INTEGER_MASK = 0xFFFFFFFFL;
int upper = Integer.MAX_VALUE;
int bottom = -1;
System.out.printf("%14s %64s%n","Upper:",Long.toBinaryString(((long)upper << 32)));
System.out.printf("%14s %64s%n","Lower:",Long.toBinaryString((long)bottom));
System.out.printf("%14s %64s%n"," Lower Masked:",Long.toBinaryString(((long)bottom)& INTEGER_MASK));
long result = ((long)upper << 32) + bottom;
System.out.printf("%14s %64s%n","Result:",Long.toBinaryString(result));
//Proper
long resultMasked = ((long)upper << 32) + (((long)bottom & INTEGER_MASK));
System.out.printf("%14s %64s%n%n","Masked",Long.toBinaryString(resultMasked));
Upper: 111_1111_1111_1111_1111_1111_1111_1111_0000_0000_0000_0000_0000_0000_0000_0000
Lower: 1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111
Lower Mask: 1111_1111_1111_1111_1111_1111_1111_1111
Result: 111_1111_1111_1111_1111_1111_1111_1110_1111_1111_1111_1111_1111_1111_1111_1111
Masked 111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111
Currently the lower word contributes 33 bits while the upper only has 32 bits. Even if the upper word is negative due to the 32 bit shift it won't wrap around. I am aware of the javadocs stating that:
Because class {@code Random} uses a seed with only 48 bits, this algorithm will not return all possible {@code long} values.
In this case it might not be detrimental but e.g. gmu's MersenneTwister implementation uses exactly this function call. Doesn't this impact the quality of random numbers generated? What am I missing here?