php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #60543 mt_rand() only reliable when ($max - $min) <= mt_getrandmax ()
Submitted: 2011-12-16 01:55 UTC Modified: 2011-12-16 07:14 UTC
Votes:2
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: daniel at danielnorton dot com Assigned:
Status: Open Package: Math related
PHP Version: 5.3.8 OS: Windows
Private report: No CVE-ID:
Have you experienced this issue?
Rate the importance of this bug to you:

 [2011-12-16 01:55 UTC] daniel at danielnorton dot com
Description:
------------
Example output from the following script that generates 16 random numbers in the 
range from -1073741823 to 2147483647. It shows that the number 1073741825 was 
repeated 4 times, or 25% of the time. This output is typical and the repeated 
number is always 1073741825. The problem is less the smaller the magnitude of 
$min.


Test script:
---------------
<?php

printf("PHP_VERSION=%s\n",PHP_VERSION);
$min = -(mt_getrandmax()>>1);
$max = mt_getrandmax();
$count = isset($argv[1])?(int)$argv[1]:16;
printf("min=%d, max=%d, count=%d\n",$min,$max,$count);

$repeat_counts = array();

for ($i=0;$i<$count;$i++) {
  $random = mt_rand($min,$max);
  if (!isset($repeat_counts[$random])) {
    $repeat_counts[$random] = 0;
  }
  $repeat_counts[$random]++;
  //printf("%12d%s\n", abs($random),($random<0)?"-":"");
}

$max_repeat_count = max($repeat_counts);
$same_value_max = array();
if ($max_repeat_count > 1) {
  foreach ($repeat_counts as $value => $repeat_count) {
    if ($repeat_count >= $max_repeat_count) {
      $same_value_max[] = $value;
    }
  }
  printf("The following number/s was/were repeated %d times (%s%%): %s\n"
    ,$max_repeat_count
    ,number_format(($max_repeat_count/$count)*100.0,1)
    ,implode(", ",$same_value_max)
    );
}


Expected result:
----------------
PHP_VERSION=5.3.8
min=-1073741823, max=2147483647, count=16



Actual result:
--------------
PHP_VERSION=5.3.8
min=-1073741823, max=2147483647, count=16
The following number/s was/were repeated 4 times (25.0%): 1073741825


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-12-16 05:11 UTC] daniel at danielnorton dot com
Further study in a 32-bit environment shows:
**1) The problem does not seem to surface when the absolute value of
both the $min and $max is within 0x3FFFFFFF. In other words, it works
as expected when the range is within -1073807359 ... 1073807359
(-0x3FFFFFFF ... 0x3FFFFFFF).

**2) The oft-repeated value is always $min & 0x7FFFFFFF, which is
always a positive integer.
 [2011-12-16 05:38 UTC] daniel at danielnorton dot com
In a 64-bit environment, although documented to be limited to
mt_getrandmax() (which is 0x7FFFFFFF in my environment), for
the purposes of diagnosis, the following is notable:

**1) The problem does not seem to surface when the absolute value
of both the $min and $max is within (PHP_INT_MAX/2)-1
(0x3FFFFFFFFFFFFFFF).

**2) The oft-repeated value is always $min & PHP_INT_MAX, which
always yields a positive integer.
 [2011-12-16 06:18 UTC] daniel at danielnorton dot com
The problem does not appear if ($max - $min) <= PHP_INT_MAX.

Perhaps this is a documentation problem and the solution might simply be to 
specify that ($max - $min) must be less than mt_getrandmax()?
 [2011-12-16 07:14 UTC] daniel at danielnorton dot com
I suggest changing the title of this to "mt_rand() only reliable when
($max - $min) <= mt_getrandmax ()".
 [2011-12-16 07:14 UTC] daniel at danielnorton dot com
-Summary: mt_rand() poor quality with large magnitude negative $min +Summary: mt_rand() only reliable when ($max - $min) <= mt_getrandmax ()
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Fri Apr 18 23:01:58 2014 UTC