In the new rand, i'm seeing evidence of uneven bias at huge ranges, but not the same kind as before. The bias seems identical for both $rand and $rands, so it's more likely caused by identical post-processing of the random inputs.

So far I've tested only the lowest bit, as it's easiest to test odd/even since $isbit doesn't work above 2^32. At largest ranges, there can be a significant difference depending on whether the range size itself is odd or even.

The Jenkins PRNG is 64bit output, and the SystemFunction036 allows the app to determine the size of the random input, but it appears to allow 64bits and even more. $rand now returns values as large as the 2^53 doubles range instead of chopping the range near 2^46.5, however the odd/even range size can determine whether all or mostly even numbers are returned.

In all these examples, the low end of the range is zero, and if $2 is 's' it uses $rands instead of $rand, showing the effects appear in both identifiers.

At the largest range, it can return numbers very close to the top of the range. However while this example shows an even split between odd/even numbers, the odd numbers are at the top of the range and the even numbers are all at the bottom of the range.

/randbiasdemo3 2^53-1


For 2^52-2 and all other range maxes in both directions where the -2 is replaced by a negative or positive even number, all returned values are even.

/randbiasdemo3 2^52-2


The odd/even bias comes-and-goes as the range size shrinks, like the previous modulo bias did. In this example and other even-numbered range maxes in both directions, 2/3rds of returned numbers are even.

/randbiasdemo3 0.67*2^51-2


60% evens: /randbiasdemo3 0.80*2^50-1
53% evens: /randbiasdemo3 0.53*2^49-1
52% evens: /randbiasdemo3 0.70*2^48-1

The above post's calculation of %throwaway_above should enable chopping a large input range down to a smaller output range without introducing bias.

alias randbiasdemo3 {
  if ($calc($1) isnum 1-9007199254740991) var -s %max $gettok($v1,1,46) | else var -s %max $calc(2^52-2)
  var %pockets 256 , %array_odd $str(0 $chr(32),%pockets) , %array_even %array_odd
  var %i 25600 , %div %max / %pockets , %odd.count 0 , %even.count 0
  if ($2 == s) echo -a using $ $+ rands
  while (%i) {
    if ($2 == s) var %rr $rands(0,%max) | else var %rr $rand(0,%max)
    if ($right(%rr,1) isin 13579) {
      var %t $calc(1+ (%rr // %div)) , %a  $gettok(%array_odd,%t,32) + 1 ,  %array_odd  $puttok(%array_odd,%a,%t,32) | inc %odd.count
    else {
      var %t $calc(1+ (%rr // %div)) , %a $gettok(%array_even,%t,32) + 1 , %array_even $puttok(%array_even,%a,%t,32) | inc %even.count
    dec %i
  echo 3 -a odd: %array_odd  = $calc($replace(%array_odd ,$chr(32),+)) $chr(22) $calc(%odd.count  *100/(%odd.count + %even.count)) $+ % $chr(22) . $stddev(%array_odd)
  echo 4 -a eve: %array_even = $calc($replace(%array_even,$chr(32),+)) $chr(22) $calc(%even.count *100/(%odd.count + %even.count)) $+ % $chr(22) . $stddev(%array_even)
alias -l stddev {
  tokenize 32 $1- | if ($0 < 2) return | var %sum 0 , %sum.of.squares 0
  var %j $0 | while (%j) { inc %sum $gettok($1-,%j,32) | dec %j }
  var %mean %sum / $0 | var %j $0 | while (%j) {
  inc %sum.of.squares $calc( ($gettok($1-,%j,32) - %mean)^2 ) | dec %j }
  var %pop.variance    $calc(%sum.of.squares / ($0   ))
  var %sample.variance $calc(%sum.of.squares / ($0 -1))
  var    $calc((%sum.of.squares / ($0   ))^.5)
  var $calc((%sum.of.squares / ($0 -1))^.5)
  var %std.err $calc( / ($0 ^ .5) )
  return count: $0 sum: %sum mean: %mean popvar: %pop.variance pop.stddev: sample.var: %sample.variance std.err: %std.err