-
Notifications
You must be signed in to change notification settings - Fork 32
Description
I wrote some basic implementations here:
https://observablehq.com/@jobleonard/xorshift (32 bits)
https://observablehq.com/@jobleonard/xorshift64
https://observablehq.com/@jobleonard/xorwow (32 bits)
Also, yesterday I came up with a hack to convert a random integer value to a random float32 or float64 value in the range [0, 1) without multiplication or division.
Try this out in the console:
u = new Uint32Array(2);
f = new Float32Array(u.buffer);
u[0] = 0b0111111100000000000000000000000;
u[1] = 0b1000000000000000000000000000000;
{ u, f };
// --> Object {
// u: Uint32Array(2) [1065353216, 1073741824]
// f: Float32Array(2) [1, 2]
// }In other words: if we first set a float32 value to 1.0, then cast to it to a uint32, we can bitmask any integer PRNG on the lower 23 bits (the mantissa, basically) to get a uniform random distribution in the range [1.0, 2.0) of Float32 (assuming the chosen integer PRNG has a uniform distribution of integer values).
All we have to do then is subtract one and we should get an approximation of a uniform distributed value in the range [0, 1.0). Ok, not entirely uniform: there will be some rounding errors of course, so I'm sure it'll introduce some bias, but it's going to be close. In JavaScript we can use a typed array to "cast" between integer and float values.
So that leads to a very simple method of using (say) a basic xorshift to create a random number in the range (0,1) (recall that xorshift cannot be zero)
const u = new Uint32Array(1);
const f = new Float32Array(u.buffer);
let x = (Math.random() * 0x100000000) | 0;
return function xorShiftF32() {
// closures never inline captured variables,
// hence this local variable to fix that
let _x = x;
_x ^= _x << 13;
_x ^= _x >> 17;
_x ^= _x << 5;
u[0] = 0b111111100000000000000000000000 | (_x & 0x7fffff);
x = _x;
return f[0] - 1;
};The first xorshift notebook contains both these "shift and subtract" versions and "shift and multiply by epsilon" versions - the multiplication one seems to be faster, but hey, it was worth a shot :)