Skip to content

GENERAL_GET_RANDOM_INT - #1221

Open
oisee wants to merge 2 commits into
open-abap:mainfrom
oisee:general-get-random-int
Open

oisee wants to merge 2 commits into
open-abap:mainfrom
oisee:general-get-random-int

Conversation

@oisee

@oisee oisee commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

A uniformly distributed integer between zero and RANGE, both included.

The bounds are measured, not inferred

The parameter documentation says "The random int will be <= range" and says nothing about the lower bound. The obvious reading — one to range — is wrong. Three hundred calls per range on an AS ABAP 1909 sandbox:

range values seen zero in
1 0..1 163 of 300 (a half)
2 0..2 105 of 300 (a third)
6 0..6 53 of 300 (a seventh)
100 0..100 3 of 300 (a hundred-and-first)

So the span is RANGE + 1 values, and the zero frequency matches 1/(range+1) at every point. Edges, same system: range = 0 answered 0; range = -5 answered -2, so a negative range is not an error and the span runs towards RANGE on whichever side of zero it lies; 2000 draws at range = 1 gave 1002 zeroes. The module declares no exceptions and the real one raises none.

The numbers come from calling the module, from a throwaway report and test class in $TMP, not from reading its implementation — an observation can be contributed, an adaptation cannot.

Why not cl_abap_random_int

That was the first attempt and it does not fit. cl_abap_random=>intinrange opens with ASSERT high > low and ASSERT low >= 0, so a range of zero dumps and every negative range dumps. Reaching the measured contract through abs( ) and a negation works and reads as cleverness hiding the intent.

It is the same Math.random either way — that is what cl_abap_random uses — and generate_sec_random in this same function group is the precedent for the plain @KERNEL form.

Tests

Four, in kernel_fugr_test: the zero range, the bounds at 6 and at -5, and that both 0 and 1 are reachable at range 1. The last is the one that tells this apart from a generator over 1..RANGE; with the naive implementation in place it fails with Expected '0', got '1', which is how it was checked.

Where it came from

An ABAP Z-machine interpreter. MiniZork plays twenty-five commands and then dies on the troll, because the random opcode calls this module and gets CX_SY_DYN_CALL_ILLEGAL_FUNC. With it the whole walkthrough plays.

Worth noting for anyone porting a Z-machine: the Z standard wants 1..range, and this module gives 0..range, so the caller has to map. That is the caller's job and not this module's — matching the real system is.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J7JpA3TGT48zBoix3Ut88s

oisee and others added 2 commits September 14, 2026 12:24
A uniformly distributed integer between zero and RANGE, both included.

The bounds are measured rather than inferred. The parameter documentation
says "The random int will be <= range" and says nothing about the lower
bound, and the obvious reading — one to range — is wrong. Three hundred
calls per range on an AS ABAP 1909 sandbox:

  range 1     values 0..1     zero in 163      (a half)
  range 2     values 0..2     zero in 105      (a third)
  range 6     values 0..6     zero in  53      (a seventh)
  range 100   values 0..100   zero in   3      (a hundred-and-first)

So the span is RANGE + 1 values. A range of zero answers zero, and a
negative range is not an error there either: -5 answered -2, so the span
runs towards RANGE on whichever side of zero it lies. The module declares
no exceptions and the real one raises none.

Written against Math.random directly rather than through
cl_abap_random_int, which was the first attempt and does not fit:
cl_abap_random=>intinrange opens with ASSERT high > low and ASSERT low >= 0,
so a range of zero dumps and every negative range dumps. Reaching the
contract through abs( ) and a negation would work and would read as
cleverness hiding the intent. It is the same Math.random either way, since
that is what cl_abap_random uses, and generate_sec_random in this group is
the precedent for the plain form.

Four tests in kernel_fugr_test: the zero range, the bounds at 6 and at -5,
and that both 0 and 1 are reachable at range 1 — the last one being what
tells this apart from a generator over 1..RANGE.
@larshp

larshp commented Sep 15, 2026

Copy link
Copy Markdown
Member

hmm, might also need to go into deprecated?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants