From 35880da8cd1ef19203b014b172e105050a5f88bd Mon Sep 17 00:00:00 2001 From: Martin Janiczek Date: Sat, 1 Aug 2026 19:00:37 +0200 Subject: [PATCH] Fix uint32 multiplication in PCG `next` and `peel` functions --- src/Random.elm | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Random.elm b/src/Random.elm index 5c68ccb..4dd230e 100644 --- a/src/Random.elm +++ b/src/Random.elm @@ -678,10 +678,10 @@ type Seed next : Seed -> Seed next (Seed state0 incr) = -- The magic constant is from Numerical Recipes and is inlined for perf. - Seed (Bitwise.shiftRightZfBy 0 ((state0 * 1664525) + incr)) incr + Seed (Bitwise.shiftRightZfBy 0 ((imul32 state0 1664525) + incr)) incr --- obtain a psuedorandom 32-bit integer from a seed +-- obtain a pseudorandom 32-bit integer from a seed peel : Seed -> Int peel (Seed state _) = -- This is the RXS-M-SH version of PCG, see section 6.3.4 of the paper @@ -689,10 +689,28 @@ peel (Seed state _) = -- the latter of which is the source of the magic constant. let word = - (Bitwise.xor state (Bitwise.shiftRightZfBy ((Bitwise.shiftRightZfBy 28 state) + 4) state)) * 277803737 + imul32 + (Bitwise.xor state (Bitwise.shiftRightZfBy ((Bitwise.shiftRightZfBy 28 state) + 4) state)) + 277803737 in Bitwise.shiftRightZfBy 0 (Bitwise.xor (Bitwise.shiftRightZfBy 22 word) word) +{-| Needed for u32 * u32 multiplications. Emulates JavaScript's Math.imul(a,b) but returns the result as an _unsigned_ 32bit number. +This would ideally live in the Bitwise module. +-} +imul32 : Int -> Int -> Int +imul32 a b = + let + aHi = Bitwise.shiftRightZfBy 16 a + aLo = Bitwise.and 0xFFFF a + bHi = Bitwise.shiftRightZfBy 16 b + bLo = Bitwise.and 0xFFFF b + low = aLo * bLo + high = Bitwise.and 0xFFFF (aHi * bLo + aLo * bHi) + in + (low + high * 0x00010000) + |> Bitwise.shiftRightZfBy 0 + {-| A `Generator` is a **recipe** for generating random values. For example, here is a generator for numbers between 1 and 10 inclusive: