Converting byte input to a decimal?
Posted: Thu May 02, 2013 10:23 pm
I have gathered three different functions for pulling random values from sources (mcrypt, openssl, and /dev/urandom). Each has a different way of 'fitting' byte inputs into a decimal range. For the purpose of being a drop-in replacement for mt_rand (but with 'more' random input), which is the most correct method for fitting, and why?
Code: Select all
function openssl_rand ($min = 0, $max = 0x7FFFFFFF)
{
$range = $max - $min;
if ($range == 0)
{
return false; // Not so random...
}
$log = log ($range, 2);
$bytes = (int) ($log / 8 ) + 1; // Length in bytes
$bits = (int) $log + 1; // Length in bits
$filter = (int) (1 << $bits) - 1; // Set all lower bits to 1
do
{
$rnd = hexdec (bin2hex (openssl_random_pseudo_bytes ($bytes, $s)));
$rnd = $rnd & $filter; // Discard irrelevant bits
}
while ($rnd >= $range);
return $min + $rnd;
}
function mcrypt_rand ($min = 0, $max = 0x7FFFFFFF)
{
$range = $max - $min;
if ($range < 0 || $range > 0x7FFFFFFF)
{
throw new RuntimeException ("Bad range");
}
$bytes = mcrypt_create_iv (4, MCRYPT_DEV_URANDOM);
if ($bytes === false || strlen($bytes) != 4)
{
throw new RuntimeException ("Unable to get 4 bytes");
}
$ary = unpack ("Nint", $bytes);
$val = $ary['int'] & 0x7FFFFFFF; // 32-bit safe
$fp = (float) $val / 2147483647.0; // Convert to [0 , 1]
return round ($fp * $range) + $min;
}
function u_rand ($min = 0, $max = 0x7FFFFFFF)
{
$bits = '';
$range = $max - $min;
$bytes = ceil ($range / 256);
$fp = @fopen ('/dev/urandom', 'rb');
if ($fp !== FALSE)
{
$bits .= @fread ($fp, $bytes);
@fclose ($fp);
}
$bitlength = strlen ($bits);
for ($i = 0; $i < $bitlength; $i++)
{
$int = 1 + (ord ($bits[$i]) % (($max - $min) + 1));
}
return $int;
}