Translating C++ program to JavaScript/asm.js does not produce the same sequence of numbers

96 views Asked by At

I took the mother of all random number generators from Agner Fog's library and attempted to make a JavaScript version of this. I expect the bit pattern to be identical but this doesn't seem to be the case and I wonder why or if I made a mistake?

The C++ code I use can be found here. It's from Agner Fog's website.

Here's my TypeScript version of this code

const u = (function () {
  const b = new ArrayBuffer(8)
  return {
    u32: new Uint32Array(b),
    u64: new BigUint64Array(b),
  }
})()

export class Random {
  state = new Uint32Array(5)

  constructor(seed?: number) {
    this.seed(seed ?? +new Date())
  }

  b() {
    const { state: x } = this

    u.u64[0] =
      2111111111n * BigInt(x[3]) +
      1492n * BigInt(x[2]) +
      1776n * BigInt(x[1]) +
      5115n * BigInt(x[0]) +
      BigInt(x[4])

    console.debug(u.u64[0])

    x[3] = x[2]
    x[2] = x[1]
    x[1] = x[0]
    x[4] = u.u32[1]
    x[0] = u.u32[0]

    return x[0]
  }

  next() {
    return (1 / 4_294_967_296) * this.b()
  }

  seed(seed: number) {
    const { state: x } = this

    let s = seed >>> 0
    // make random numbers and put them into the buffer
    for (let i = 0; i < 5; i++) {
      s = s * 29943829 - 1
      x[i] = s
    }

    console.debug(x)

    // randomize some more
    for (let i = 0; i < 19; i++) {
      this.b()
    }
  }
}

I managed to get it down to the the state initialization but I cannot understand why the output from the C++ program is 4294967295, 4265023466, 1627457073, 3925469700, 2226377299 but the TypeScript program is giving me 4294967295, 4265023466, 1627457073, 3925868544, 0. Only the first 3 numbers get computed exactly the same. Any help trying to understand this is much appreciated.

1

There are 1 answers

5
John Leidegren On

Ah, I figured it out. I have to use Math.imul to get correct results. Can't do C-like integer multiplication without it.

So, this:

for (let i = 0; i < 5; i++) {
  s = s * 29943829 - 1
  x[i] = s
}

...changes into:

for (let i = 0; i < 5; i++) {
  s = Math.imul(s, 29943829) - 1
  x[i] = s
}

I tried stuff like (s * 29943829) >>> 0 but this is not enough to force C style integer multiplication.