Perform 32-bit random if the bound fits into 32 bits at runtime.
Patch: [random32.patch](/uploads/b8ca40ddbc26384b567d347f97b0ff24/random32.patch).
Motivating example:
```pascal
{$mode objfpc}
uses
Math;
var
shuffleBag: array of int32;
i: SizeInt;
begin
write('Shuffle: ');
shuffleBag := [1, 2, 3, 4, 5, 6, 7];
repeat
i := random(length(shuffleBag));
write(shuffleBag[i], ' ');
delete(shuffleBag, i, 1);
until length(shuffleBag) = 0;
writeln(LineEnding, 'Nefia boss hex resistance = ', RandomRange(0, 100), '%');
end.
```
Output on `x86-32` before the patch (and everywhere after the patch):
```
Shuffle: 7 4 5 6 2 1 3
Nefia boss hex resistance = 64%
```
Output on `x86-64` before the patch:
```
Shuffle: 7 5 2 6 3 4 1
Nefia boss hex resistance = 90%
```
“Shuffle” part might be understandable: `length()` is a `SizeInt`, which is an `int32` on 32 bits and `int64` on 64 bits, so they call different `random` overloads which work differently in general and draw different amounts of `uint32` from the underlying generator, so what’d you expect. However, this is very inconvenient if you seek reproducibility across bitnesses, and `random(int32(length()))` is an extremely annoying workaround: in addition to being cumbersome, it, well, truncates `length()` to 32 bits which means all of its instances have the potential to become hard-to-find bugs.
Moreover, `int64` is a native type on 64 bits, so calculating the `random` bound in place (`a, b: int32; random(a + b)`) will often upcast it to 64 bits according to FPC rules, leading to more unobvious results, like bitness-dependent `Math.RandomRange` which does exactly that under the hood.
I personally did this change with my own RNG a thousand years ago, and remembered about the same problem with standard `System.Random` because I found [this issue](https://gitlab.com/freepascal.org/fpc/source/-/issues/31693) while searching for something unrelated. It had a completely different complaint which is _not_ fixed by my patch, but I still think it can be closed because it’s long out of date (and the complaint would remain **DUBIOUS** regardless of that, like, the only reason to do that is validating the underlying generator, which is not going to lose much of the reliability from checking every other value).
As you might suspect from having a glimpse at `random(int32)` and `random(int64)` implementations, this is also an optimization to `random(int64)`: on my computer, it becomes 50–80% faster (not inlining `random(int32)` reduces this to 30–50%) when its argument fits into `int32`, at the cost of 5–10% slowdown when it doesn’t.
issue