Skip to content

Avoid ldexp call when converting random numbers to double - #4159

Open
GuySten wants to merge 1 commit into
openmc-dev:developfrom
GuySten:claude/prn-avoid-ldexp
Open

GuySten wants to merge 1 commit into
openmc-dev:developfrom
GuySten:claude/prn-avoid-ldexp

Conversation

@GuySten

@GuySten GuySten commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Description

prn() converts the 64-bit output of the PCG generator to a double in [0, 1) with ldexp(result, -64). This compiles to an out-of-line library call on every random number, and profiling shows it is a noticeable fraction of run time (scalbn/__scalbn together were ~7% of a heavy water problem with thermal scattering and ~5% of a gamma shielding problem).

This PR replaces it with a multiplication by the exact power of two 0x1p-64. Because the conversion from uint64_t to double is unchanged and scaling by a power of two is exact (the result is either zero or at least 2⁻⁶⁴, so it cannot underflow), the result is bit-for-bit identical to ldexp. I checked this on 2×10⁸ inputs, including edge cases such as 0, 2⁵³±1 and 2⁶⁴−1, with no mismatches. The now-unused <cmath> include is removed.

Random number sequences and simulation results are unchanged; the full unit and regression test suite gives the same results as develop, and tallies were bit-for-bit identical in the timing runs below.

Transport time, single thread, average of three runs:

Problem develop this PR Change
Watt source in a 100 cm heavy water sphere with c_D_in_D2O, 20,000 histories 14.13 s 13.13 s −7.1%
Cs-137 photon source in a W/Pb cask, 100,000 histories 21.29 s 20.39 s −4.2%

Checklist

  • I have performed a self-review of my own code
  • I have run clang-format (version 18) on any C++ source files (if applicable)
  • I have followed the style guidelines for Python source files (if applicable)
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works (if applicable)

prn() converted the 64-bit output of the generator to a double in [0, 1)
with ldexp(result, -64), which compiles to an out-of-line library call on
every random number. Multiplying by the exact power of two 0x1p-64 gives
bit-for-bit identical results (the scaling is exact and cannot underflow)
and compiles to a single instruction.

Random numbers and simulation results are unchanged. Transport time is
reduced by about 7% for a neutron source in a heavy water sphere with
thermal scattering and by about 4% for a gamma shielding problem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RN7JroEkrbkLKVHbMDap9
@GuySten
GuySten requested a review from paulromano October 2, 2026 21:24
@GuySten
GuySten marked this pull request as ready for review October 2, 2026 23:18

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants