1

In the AsemblyScript book it mentions that Math.random() takes a seed and returns an <f64> value. I just need a random <u64> value. How do I achive that?

I tried

(Math.random() * 0xffffffffffffffff) as u64
<u64>(<f64>Math.random() * <f64>0xffffffffffffffff)
(<f64>Math.random() * <f64>0xffffffffffffffff) as u64

or with f64.MAX_VALUE in the place of 0xffffffffffffffff and whatnot.

but I keep getting 0.

I can get <U32> random values just fine but When I multiply two <U32> random values I get like 52 random bits and the rest is 0. I understand why this happens from my JS background, still from the typed struct and lower level abstractions of AS I hoped to get no friction.

How exactly I can obtain a <u64> random integer properly with AssemblyScript?

Edit:

I think I finally got it doing like

(<u64>(Math.random() * u32.MAX_VALUE) << 32) | <u32>(Math.random() * u32.MAX_VALUE)

but is this really how it should be done?

Redu
  • 25,060
  • 6
  • 56
  • 76
  • Using `u32.random` as a strawman for a random u32: would `((u32.random() as u64) << 32) | (u32.random as u64)` work? Or do AssemblyScript bitwise operators only work on 32-bit numbers, like in JS? – Bbrk24 Nov 17 '22 at 19:39
  • @Bbrk24 Nope... just tested `((Math.random() << 32) * 0xffffffffffffffff) as u64` still yields 0. – Redu Nov 17 '22 at 19:47
  • Are you sure `Math.random()` works? `Math.random()` generates in the range [0, 1), which will always yield 0 when converted to an integer. I imagined `u32.random` being more like `(Math.random() * 0xffffffff)`. – Bbrk24 Nov 17 '22 at 20:02
  • @Bbrk24 I don't convert `Math.random()` to integer before i multiply it with a sufficiently large number and yea `(Math.random() * u32.MAX_VALUE)` just works fine. – Redu Nov 17 '22 at 20:10
  • That is what I meant by `u32.random`. I was just using it as a shorthand since I didn't know the exact syntax/implementation. – Bbrk24 Nov 17 '22 at 20:33
  • @Bbrk24 I now got your point in your first comment but with all those types and whatnot I think there should be a more straightforward way. Why wouldn't you be able to perform this operation in 64 bits right on? Especially `Math.random() * 0xffffffffffffffff` yielding 0 is confusing given that `Math.random()` returns an ``. Perhaps a bug or something that I badly miss in general binary representations of numbers. – Redu Nov 17 '22 at 21:12

1 Answers1

2

After reading your question, I decided to look into how AssemblyScript implements Math.random in the standard library, to see if I can get some inspiration to solve your problem.

Funny, enough, it seems to use the murmurhash3 and some custom extra hashes. Right before the return, it reinterprets a u64 value into a f64, after some extra processing:

    let r = (s0 >> 12) | 0x3FF0000000000000;
    return reinterpret<f64>(r) - 1;

I got curious if we can use that u64 value directly as a number in a random sequence, so I reworked the bits involved and posted it on github here, but the main function is:

export function randomU64(): u64 { // see: v8/src/base/utils/random-number-generator.cc
  if (!random_seeded) seedRandom(reinterpret<i64>(0)); // TODO: for now, the seed is always 0.
  let s1 = random_state0_64;
  let s0 = random_state1_64;
  random_state0_64 = s0;
  s1 ^= s1 << 23;
  s1 ^= s1 >> 17;
  s1 ^= s0;
  s1 ^= s0 >> 26;
  random_state1_64 = s1;
  return s0;
}

A very brief test shows that, at least at a glance, it produces fairly good random results:

Test randomU64 distribution. All values should be fairly close.
Map(19) {
  0n => 987,
  1n => 495, -1n => 515, 2n => 542, -2n => 489,
  3n => 518, -3n => 495, 4n => 479, -4n => 510,
  5n => 513, -5n => 497, 6n => 473, -6n => 505,
  7n => 468, -7n => 528, 8n => 501, -8n => 472,
  9n => 519, -9n => 494
}
TachyonicBytes
  • 700
  • 1
  • 8
  • Thank you for your attempt. It's interesting. I have to test against the `.popcnt()` function to see if we get 32 as an average in multiple tries for an `` type input. I think the `seedRandom()` functionality gets implemented like `(Math.random() * Date.now()) as u64`. – Redu Nov 27 '22 at 18:51
  • I didn't think of `popcnt` for randomness. It gets `31.9805` average `popcnt`, on the next 10000 values, so it seems close enough. I pushed another test to the repo. – TachyonicBytes Nov 27 '22 at 22:32
  • Looks fine but since we always start seeding with 0 yielding golden ratio, it's quite deterministic. In order to introduce some enthropy I rephrased the line 36 at repo `if (!random_seeded) seedRandom(reinterpret(Math.random()*Date.now()));`. Now even after a recompile we get different random numbers. The standard deviation and mean of 10 tests each with 1e6 x 64 bit popcnts are 0.0059% and 32.0019481 respectively. – Redu Nov 28 '22 at 11:09