且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

PHP的mt_rand()最小值是多少,以及如何在32位Linux机器上计算32位整数

更新时间:2023-11-20 20:03:04

TL; DR:要获取可能的整数范围内的随机整数,请使用:

function random_integer() {
    $min = defined('PHP_INT_MIN') ? PHP_INT_MIN : (-PHP_INT_MAX-1);
    return mt_rand($min, -1) + mt_rand(0, PHP_INT_MAX);
}

对于PHP 7,您可以使用 random_int() ./p>


内幕( 1 2 ),PHP是这样做:

$number = random_number_between_0_and_0x7FFFFFFF_using_Mersenne_Twister;
$number = $min + (($max - $min + 1.0) * ($number / (0x7FFFFFFF + 1.0)));

通知$max - $min.当max设置为最高值并且min为负数时,将发生溢出.因此,最大范围PHP_INT_MAX.如果最大值为PHP_INT_MAX,则最小值必须为0.

现在来看背景故事. PHP实现了32位Mersenne Twister算法.这为我们提供了[0和2 ^ 31-1)之间的随机整数.如果您要求其他任何范围,PHP会使用简单的装箱功能对数字进行缩放.该分箱功能包括一个减法,该减法会导致溢出以及此问题.

因此,如果要获得比PHP中整数表示的范围更大的范围,则必须将间隔加在一起,如下所示:

mt_rand(PHP_INT_MIN, -1) + mt_rand(0, PHP_INT_MAX);

请注意,PHP_INT_MIN自PHP 7起可用,因此您需要在此之前为您的环境计算一个合适的最小值.


顺便说一句,注意getrandmax()返回的是2 ^ 31-1.人们错误地认为,在64位计算机上,getrandmax()将返回2 ^ 63-1.这不是真的. getrandmax()返回算法将返回的最大整数,该整数始终为2 ^ 31-1.

What is the minimum value allowed for mt_rand()? Is it the same value for 32 bit and 64 bit machines? How could I generate a 32 bit integer using mt_rand() (note that it doesn't need to be highly random)?

BACKGROUND WHY I AM ASKING: I have a 64 bit development physical server and a 32 bit production VPS. Just realized the production server was not generating PKs spanning the full range. To figure out what is going on, I ran the following script. The 64 bit machine never (or at least I've never witnessed) matches, but the 32 bit matches about 50% of the time.

<?php

date_default_timezone_set('America/Los_Angeles');
ini_set('display_errors', 1);
error_reporting(E_ALL);

$count=0;
for ($i = 0; $i <= 10000; $i++) {
    $rand=2147483648+mt_rand(-2147483647,2147483647); //Spans 1 to 4294967295 where 0 is reserved
    if($rand==2147483649){$count++;}
}
echo('mt_getrandmax()='.mt_getrandmax().' count='.$count);

output

mt_getrandmax()=2147483647 count=5034

TL;DR: To get a random integer in the full range of possible integers, use:

function random_integer() {
    $min = defined('PHP_INT_MIN') ? PHP_INT_MIN : (-PHP_INT_MAX-1);
    return mt_rand($min, -1) + mt_rand(0, PHP_INT_MAX);
}

For PHP 7, you can use random_int().


Under the hood (1, 2), PHP is doing this:

$number = random_number_between_0_and_0x7FFFFFFF_using_Mersenne_Twister;
$number = $min + (($max - $min + 1.0) * ($number / (0x7FFFFFFF + 1.0)));

Notice $max - $min. When max is set to the top end and min is anything negative, an overflow occurs. Therefore, the maximum range is PHP_INT_MAX. If your maximum value is PHP_INT_MAX, then your minimum is necessarily 0.

Now for the back story. PHP implements the 32-bit Mersenne Twister algorithm. This gives us random integers between [0, and 2^31-1). If you ask for any other range, PHP scales that number using a simple binning function. That binning function includes a subtraction that can lead to overflow, and this problem.

Thus if you want to get a range larger than could be represented by an integer in PHP, you have to add intervals together, like so:

mt_rand(PHP_INT_MIN, -1) + mt_rand(0, PHP_INT_MAX);

Note that PHP_INT_MIN is available since PHP 7, so you'll need to calculate a suitable minimum for your environment before then.


As an aside, notice that 2^31-1 is what getrandmax() returns. People mistakenly believe that on a 64-bit machine getrandmax() will return 2^63-1. That's not true. getrandmax() returns the maximum integer the algorithm will return, which is always 2^31-1.