go-lua ships with the math library bound to Go's math/rand (v1), which uses
a Lagged-Fibonacci generator. Reference Lua 5.4 specifies xoshiro256** as the
PRNG backing math.random / math.randomseed. The two algorithms produce
different output streams for the same seed, and Lua 5.4 also exposes a richer
math.randomseed API that go-lua does not implement.
The PUC-Rio Lua 5.4 test suite (lua-tests/math.lua) carries 39 lines of
-- SKIP (go-lua uses different PRNG): markers around the random-related
sections that depend on these differences.
What the SKIPs cover
Block A — bit-exact stream after a known seed (~lua-tests/math.lua:815-841)
math.randomseed(1007)
-- the first call after seed 1007 should return 0x7a7040a5a323c9d6 in Lua 5.4
assert(math.random(0) == 0x7a7040a5a323c9d6)
This is impossible to satisfy with Go's math/rand (v1) — different algorithm,
different bits. Requires implementing xoshiro256** to pass.
Block B — Lua 5.4 randomseed() 0-arg and 2-arg signatures (~lua-tests/math.lua:843-853)
local x, y = math.randomseed() -- no args: pick a random seed, return it
local res = math.random(0)
math.randomseed(x, y) -- restore from two-word state
assert(math.random(0) == res) -- should reproduce the stream
go-lua's current math.randomseed (math.go:~245) takes exactly one argument
and returns nothing. Lua 5.4 accepts 0/1/2 args and returns the two 64-bit
state words that seeded the generator.
Suggested fix
Implement xoshiro256** as the backing PRNG and extend the math.randomseed
binding. Rough sketch:
type xoshiro256ss struct{ s [4]uint64 }
func (x *xoshiro256ss) Uint64() uint64 {
result := bits.RotateLeft64(x.s[1]*5, 7) * 9
t := x.s[1] << 17
x.s[2] ^= x.s[0]; x.s[3] ^= x.s[1]
x.s[1] ^= x.s[2]; x.s[0] ^= x.s[3]
x.s[2] ^= t
x.s[3] = bits.RotateLeft64(x.s[3], 45)
return result
}
Plus a splitmix64-based seed-expansion routine that mirrors Lua 5.4's
setseed exactly (see lmathlib.c setseed in upstream Lua 5.4 source),
so that randomseed(1007) produces the spec-mandated 0x7a7040a5a323c9d6.
The two-word state exposed by randomseed() corresponds to two of the four
xoshiro256 state words (Lua 5.4 returns s[0] and s[1]).
What this would enable
- Drop all 39
-- SKIP (go-lua uses different PRNG): markers in
lua-tests/math.lua and run those tests for real coverage.
- Scripts that move between PUC-Rio Lua and go-lua get bit-identical
math.random behaviour.
- Lua 5.4's standard
math.randomseed() 0-arg / 2-arg signatures work,
which is what scripts that need to snapshot-and-restore PRNG state expect.
Out of scope for this issue
The regression where math.randomseed(N) was a no-op against Go 1.20+'s
autoseeded global source was a separate functional bug — fixed in commit
f694f4c ("math: route random/randomseed through a package-local
*rand.Rand"). This issue is purely about algorithm and API conformance,
which is independent of that fix.
Related
- Lua 5.4 reference manual: §6.7 Mathematical Functions (
math.random /
math.randomseed).
- Upstream PUC-Rio Lua source:
src/lmathlib.c — the setseed,
nextrand, and randomseed C functions are the reference implementation.
- xoshiro256** reference C code: https://prng.di.unimi.it/xoshiro256starstar.c
- Existing skip markers:
grep -n "SKIP (go-lua uses different PRNG)" lua-tests/math.lua
go-lua ships with the
mathlibrary bound to Go'smath/rand(v1), which usesa Lagged-Fibonacci generator. Reference Lua 5.4 specifies xoshiro256** as the
PRNG backing
math.random/math.randomseed. The two algorithms producedifferent output streams for the same seed, and Lua 5.4 also exposes a richer
math.randomseedAPI that go-lua does not implement.The PUC-Rio Lua 5.4 test suite (
lua-tests/math.lua) carries 39 lines of-- SKIP (go-lua uses different PRNG):markers around the random-relatedsections that depend on these differences.
What the SKIPs cover
Block A — bit-exact stream after a known seed (~
lua-tests/math.lua:815-841)This is impossible to satisfy with Go's
math/rand(v1) — different algorithm,different bits. Requires implementing xoshiro256** to pass.
Block B — Lua 5.4
randomseed()0-arg and 2-arg signatures (~lua-tests/math.lua:843-853)go-lua's current
math.randomseed(math.go:~245) takes exactly one argumentand returns nothing. Lua 5.4 accepts 0/1/2 args and returns the two 64-bit
state words that seeded the generator.
Suggested fix
Implement xoshiro256** as the backing PRNG and extend the
math.randomseedbinding. Rough sketch:
Plus a
splitmix64-based seed-expansion routine that mirrors Lua 5.4'ssetseedexactly (seelmathlib.csetseedin upstream Lua 5.4 source),so that
randomseed(1007)produces the spec-mandated0x7a7040a5a323c9d6.The two-word state exposed by
randomseed()corresponds to two of the fourxoshiro256 state words (Lua 5.4 returns
s[0]ands[1]).What this would enable
-- SKIP (go-lua uses different PRNG):markers inlua-tests/math.luaand run those tests for real coverage.math.randombehaviour.math.randomseed()0-arg / 2-arg signatures work,which is what scripts that need to snapshot-and-restore PRNG state expect.
Out of scope for this issue
The regression where
math.randomseed(N)was a no-op against Go 1.20+'sautoseeded global source was a separate functional bug — fixed in commit
f694f4c("math: route random/randomseed through a package-local*rand.Rand"). This issue is purely about algorithm and API conformance,
which is independent of that fix.
Related
math.random/math.randomseed).src/lmathlib.c— thesetseed,nextrand, andrandomseedC functions are the reference implementation.grep -n "SKIP (go-lua uses different PRNG)" lua-tests/math.lua