fix(fsharp): mulberry32 wraps through uint64 so the Fable build matches the fixtures

Fable compiles uint32 * and + to plain JavaScript arithmetic (no wrap, inexact
above 2^53); 2 of 37 RNG fixture cases matched in the browser build. uint64
helpers are exact on .NET and in Fable (BigInt).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-29 01:04:10 +01:00
co-authored by Claude Opus 5.5
parent ab8abaaa77
commit 527b2cd157
2 changed files with 16 additions and 8 deletions
+5 -3
View File
@@ -17,12 +17,14 @@ let map (f: 'a -> 'b) (r: Random<'a>) : Random<'b> = failwith "TODO m04"
/// Run `r`, feed its value to `f` to pick the next Random, and run that with the seed `r` left behind.
let bind (f: 'a -> Random<'b>) (r: Random<'a>) : Random<'b> = failwith "TODO m04"
/// The generator (mulberry32). uint32 arithmetic already wraps around at 2^32 and `>>>` on a uint32 is an
/// unsigned shift. With `s = seed + 0x6D2B79F5u`:
/// The generator (mulberry32). With `s = seed + 0x6D2B79F5u`:
/// t = (s ^^^ (s >>> 15)) * (s ||| 1u)
/// t = t ^^^ (t + (t ^^^ (t >>> 7)) * (t ||| 61u))
/// value = t ^^^ (t >>> 14)
/// It returns `value` and the new seed `s`.
/// It returns `value` and the new seed `s`. Every `*` and `+` must wrap around at 2^32, and `>>>` on a uint32 is an
/// unsigned shift. On .NET uint32 `*` and `+` already wrap, but the browser build (Fable) turns them into plain
/// JavaScript arithmetic that does not, so write them as small helpers that go through uint64 and back, e.g.
/// `let mul (a: uint32) (b: uint32) = uint32 (uint64 a * uint64 b)`; that is exact on both.
/// (The stub is wrapped in `Random` so the TODO only fires when the generator is run.)
let nextUint32: Random<uint32> = Random(fun _ -> failwith "TODO m04")