Repository navigation
feat(stdlib): make rand seedable with OTP 29's algorithms - #14
Merged
Merged
Conversation
trunc/1, round/1, ceil/1 and floor/1 cast the float to a 64-bit integer first, so any float at or beyond 2^63 in magnitude became 2^63 - 1 or -2^63 instead of the exact integer OTP returns. rand's exs64, exsplus and exs1024 algorithms truncate such floats for ranges above 2^58 or 2^64. The float_integer_large fixture checks the four conversions against OTP 29 through direct, apply and remote fun calls.
rand:uniform/1 was a runtime built-in drawing from an unseedable generator, and no other rand function existed, so seeded programs such as PropEr runs drew different numbers on every run. erlang/stdlib/rand.erl is now OTP 29's rand module ported to Nera: seed/1,2, seed_s/1,2, export_seed/0 and export_seed_s/1, uniform/0,1 and uniform_s/1,2, uniform_real, bytes, normal, shuffle, jump and the mwc59 and splitmix64 utilities, for every OTP algorithm. A seed gives the same numbers as on OTP 29. The built-in is removed so calls reach the module. The module needs math:exp/1, math:log/1 and math:pow/2, now runtime built-ins that raise badarg and badarith as OTP does. rand_regression compares seeded draws, states, jumps, shuffles and error reasons for every algorithm and seed form with values generated on OTP 29 by scripts/generate-rand-regression.escript. math_exp_log_pow covers the math functions. Both run natively and on BEAM.
FEATURES.md lists the rand functions and the new math functions, the changelog records the change and its build, size and speed cost, and the test index lists the three new fixtures. Cowlib 2.22.0's seeded date property was blocked natively because rand:seed/2 did not control the rand:uniform/1 built-in. With the built-in gone it passes 6,000 trials per engine with identical traces, so the compatibility table, known limits and Cowlib notes say so and give the reproduction command.
mremond
force-pushed
the
fm/task-b01e1756
branch
from
November 12, 2026 17:44
75075f8 to
2f269d7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
rand:uniform/1 could not be seeded and no other rand function existed, so seeded programs such as PropEr runs drew different numbers on every run.
What changed
Before and after
Before: A program calling rand:seed/2 failed to build because the rand module was not available, and two runs of rand:uniform(100) printed different numbers.
After: A seeded program prints the same numbers on every run, equal to OTP 29.1.2's output, and Cowlib's date property passes 6,000 trials per engine with identical traces.
This changes what compiled programs compute and print in a terminal; nothing on screen changed, so the evidence is program output and test results.
Checked
How it was checked
cargo build --release --locked, then cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked --no-fail-fast (2,142 passed)
python3 scripts/test-allocator-config.py
./target/release/nera test -j 1 --timeout 30 (471 passed, 2 skipped, 0 failed)
nera test rand_regression math_exp_log_pow float_integer_large rand_uniform, ten times in a row, and the same four modules on OTP 29 ten times
python3 tests/integration/cowlib/native_date.py --pin 2.22.0 with the pinned PropEr and OTP 27.3 rand and ordsets sources
After bringing the branch up to date with the atom-quoting fix on main: rebuild, clippy, the runtime tests (755 passed) and the rand, math, float, sqrt and atom-quoting fixtures pass
Risk
Risk above low: Blast radius, medium — Every program compiles the rand module; four float conversions and three math functions change; Behavior, medium — New rand functions are opt-in; uniform/1 now uses OTP's generator and is slower; Novelty, medium — A 2,188-line module ported from OTP beside the existing stdlib
Full risk assessment
Not done
A normal draw takes about 2 µs natively (37 ns on BEAM): each draw rebuilds a 256-entry table because Nera has no constant literal storage.
Every program compiles rand, about 0.1 s and 0.57 MB more, even when it never calls it.