Skip to content

Would there be any interest in xorshift32, xorshift64 and xorwow32? #39

@JobLeonard

Description

@JobLeonard

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 :)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions