I had this written up already, so I'll go ahead an post it.

With random numbers, it's hard to state that something is 'working well' just because you can't easily see problems. When problems are visible at huge ranges but aren't at small ranges doesn't mean the problem isn't there at the smaller ranges.

If it's not reasonable to try to obtain a uint64 %throwaway_above where the range value could be a 53bit integer, a better method than trying to round to a double, would be something similar to the XOR folding mentioned in the FNV site. If trying to reduce a 64bit data input to a number in the 0-2^53-1 range where $calc can accurately do math, it can first split the 64 bits into group #1 having 53 bits and group #2 having 11 bits. The 11 bits of group#2 could be XOR'ed against the lowest 11 bits of group#1. This would create a 53 bit value where each of the 0-1FFFFFFFFFFFF values have the same 2^11 number of possible inputs.

Once you have the 53 bit value, then $calc can accurately calculate the %throwaway_above value needed for the $rand(N1,N2) range as long as none of %range or N1 or N2 is outside the accuracy range.

Once the 64bit number has been narrowed to a 53bit number, using the %throwaway_above value ensures that all outputs have an equal number of outputs, because it throws away extra inputs only available to some of the outputs. Where %in_max is a 52/53 bit number, the number of values being thrown away can never exceed half of %in_max and can never exceed (%range -1). Worst case scenario is throwing away nearly half the first random number, and for smaller ranges it should rarely come into play.

Any method of taking a 2^N size input and applying it against an output range whose size is not a 2^N size is going to have some bias in it, even if it's not easily detectable at 'normal' ranges. Other uses like the 64-bit salt/iv used by $encode, or the 128-bit salt used by $zip, could use the raw input used by $rands().