php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #55519 mt_rand only generetes even 64 bits numbers
Submitted: 2011-08-27 09:09 UTC Modified: 2011-08-28 04:36 UTC
From: sergio dot bobillier at gmail dot com Assigned: aharvey (profile)
Status: Closed Package: Documentation problem
PHP Version: Irrelevant OS: Linux (Ubuntu 11.04 x64)
Private report: No CVE-ID: None
 [2011-08-27 09:09 UTC] sergio dot bobillier at gmail dot com
Description:
------------
I was doing a lame RSA implementation when I discovered that mt_rand only generates even numbers when the number is a 64 bits integer.

The Test script shows the script I used to generate two 64 bits integers p and q with mt_rand. I was doing primality test only with odd numbers since even numbers are not prime but the script never do the primality test because all the generated numbers are even.

I thought this might be an integer overflow problem but I'm doing this on a 64 bits system and the var_dump function shows that the variables are actually integers like so:

int(2117698586505379840)
int(1690740540401778688)
int(3279295862706012160)
int(4526730875131396096)
int(3188107862534520832)
int(1914802535743356928)
int(4172596828035874816)
int(4559249790717657088)
int(1938449129751445504)
int(1859647742213619712)

Test script:
---------------
function is_prime($num)
{
	// See if the number is prime
	echo "Primality test";
	return true;
}

function gen_keys()
{
	// 1. find two 64 bits primes p and q
		
	$p = 0;
	$q = 0;
	
	while(!$p)
	{
		$p = mt_rand(72057594037927936, 4611686018427387904);

		// debugging:
		var_dump($p);

		if($p % 2 == 0)
		{
			$p = 0;
		}
		else
		{

			// Primality test in another function
			// (not important here)

			echo "odd";

			if(!is_prime($p))
				$p = 0;
		}
	}
	
	echo $p;
}

Expected result:
----------------
I expect the mt_rand to generate some odd numbers and make the script go through the primality test or at least print "odd"

Actual result:
--------------
The script doesn't reach the primality or print "odd" test because all generated numbers are even.

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-08-28 03:40 UTC] aharvey@php.net
-Status: Open +Status: Assigned -Assigned To: +Assigned To: aharvey
 [2011-08-28 03:40 UTC] aharvey@php.net
Verified on 64-bit OS X, at least beyond a certain set of mt_rand() bounds. Not entirely sure what those actual bounds are yet.
 [2011-08-28 03:48 UTC] aharvey@php.net
OK, so this behaves normally for bounds up to 2^32, then does weird things after that. Given this script:

<?php
$iterations = 1000;

function mt_check($lower, $upper) {
    global $iterations;

    $stats = array_reduce(array_map(function ($v) use ($lower, $upper) {
        return mt_rand($lower, $upper);
    }, range(1, $iterations)), function ($acc, $v) {
        $v % 2 ? $acc['odd']++ : $acc['even']++;
        return $acc;
    }, array('odd' => 0, 'even' => 0));

    echo "Stats for $iterations iterations of $lower..$upper: $stats[odd] odd; $stats[even] even\n";
}

foreach (range(2, 62) as $pow) {
    mt_check(pow(2, $pow - 1), pow(2, $pow));
}
?>

The results are this:

Stats for 1000 iterations of 2..4: 319 odd; 681 even
Stats for 1000 iterations of 4..8: 414 odd; 586 even
Stats for 1000 iterations of 8..16: 452 odd; 548 even
Stats for 1000 iterations of 16..32: 443 odd; 557 even
Stats for 1000 iterations of 32..64: 475 odd; 525 even
Stats for 1000 iterations of 64..128: 492 odd; 508 even
Stats for 1000 iterations of 128..256: 486 odd; 514 even
Stats for 1000 iterations of 256..512: 482 odd; 518 even
Stats for 1000 iterations of 512..1024: 505 odd; 495 even
Stats for 1000 iterations of 1024..2048: 502 odd; 498 even
Stats for 1000 iterations of 2048..4096: 480 odd; 520 even
Stats for 1000 iterations of 4096..8192: 485 odd; 515 even
Stats for 1000 iterations of 8192..16384: 501 odd; 499 even
Stats for 1000 iterations of 16384..32768: 513 odd; 487 even
Stats for 1000 iterations of 32768..65536: 508 odd; 492 even
Stats for 1000 iterations of 65536..131072: 498 odd; 502 even
Stats for 1000 iterations of 131072..262144: 510 odd; 490 even
Stats for 1000 iterations of 262144..524288: 514 odd; 486 even
Stats for 1000 iterations of 524288..1048576: 498 odd; 502 even
Stats for 1000 iterations of 1048576..2097152: 496 odd; 504 even
Stats for 1000 iterations of 2097152..4194304: 488 odd; 512 even
Stats for 1000 iterations of 4194304..8388608: 497 odd; 503 even
Stats for 1000 iterations of 8388608..16777216: 493 odd; 507 even
Stats for 1000 iterations of 16777216..33554432: 498 odd; 502 even
Stats for 1000 iterations of 33554432..67108864: 458 odd; 542 even
Stats for 1000 iterations of 67108864..134217728: 500 odd; 500 even
Stats for 1000 iterations of 134217728..268435456: 499 odd; 501 even
Stats for 1000 iterations of 268435456..536870912: 480 odd; 520 even
Stats for 1000 iterations of 536870912..1073741824: 512 odd; 488 even
Stats for 1000 iterations of 1073741824..2147483648: 487 odd; 513 even
Stats for 1000 iterations of 2147483648..4294967296: 492 odd; 508 even
Stats for 1000 iterations of 4294967296..8589934592: 0 odd; 1000 even
Stats for 1000 iterations of 8589934592..17179869184: 0 odd; 1000 even
Stats for 1000 iterations of 17179869184..34359738368: 0 odd; 1000 even
Stats for 1000 iterations of 34359738368..68719476736: 0 odd; 1000 even
Stats for 1000 iterations of 68719476736..137438953472: 0 odd; 1000 even
Stats for 1000 iterations of 137438953472..274877906944: 0 odd; 1000 even
Stats for 1000 iterations of 274877906944..549755813888: 0 odd; 1000 even
Stats for 1000 iterations of 549755813888..1099511627776: 0 odd; 1000 even
Stats for 1000 iterations of 1099511627776..2199023255552: 0 odd; 1000 even
Stats for 1000 iterations of 2199023255552..4398046511104: 0 odd; 1000 even
Stats for 1000 iterations of 4398046511104..8796093022208: 0 odd; 1000 even
Stats for 1000 iterations of 8796093022208..17592186044416: 0 odd; 1000 even
Stats for 1000 iterations of 17592186044416..35184372088832: 0 odd; 1000 even
Stats for 1000 iterations of 35184372088832..70368744177664: 4 odd; 996 even
Stats for 1000 iterations of 70368744177664..140737488355328: 4 odd; 996 even
Stats for 1000 iterations of 140737488355328..281474976710656: 5 odd; 995 even
Stats for 1000 iterations of 281474976710656..562949953421312: 20 odd; 980 even
Stats for 1000 iterations of 562949953421312..1125899906842624: 34 odd; 966 even
Stats for 1000 iterations of 1125899906842624..2251799813685248: 66 odd; 934 even
Stats for 1000 iterations of 2251799813685248..4503599627370496: 141 odd; 859 even
Stats for 1000 iterations of 4503599627370496..9007199254740992: 272 odd; 728 even
Stats for 1000 iterations of 9007199254740992..18014398509481984: 0 odd; 1000 even
Stats for 1000 iterations of 18014398509481984..36028797018963968: 0 odd; 1000 even
Stats for 1000 iterations of 36028797018963968..72057594037927936: 0 odd; 1000 even
Stats for 1000 iterations of 72057594037927936..144115188075855872: 0 odd; 1000 even
Stats for 1000 iterations of 144115188075855872..288230376151711744: 0 odd; 1000 even
Stats for 1000 iterations of 288230376151711744..576460752303423488: 0 odd; 1000 even
Stats for 1000 iterations of 576460752303423488..1152921504606846976: 0 odd; 1000 even
Stats for 1000 iterations of 1152921504606846976..2305843009213693952: 0 odd; 1000 even
Stats for 1000 iterations of 2305843009213693952..4611686018427387904: 0 odd; 1000 even

As any good engineer who's baffled would say: hmmm, that's interesting.
 [2011-08-28 04:02 UTC] aharvey@php.net
-Type: Bug +Type: Documentation Problem -Package: Math related +Package: Documentation problem
 [2011-08-28 04:02 UTC] aharvey@php.net
So, the obvious answer here is that MT is giving us 32-bit integers, and multiplying them out to 64-bit results in even numbers only. That's sort of right, but as the results above show, there are certain ranges above 2^32 where we get _some_ odd numbers, even if it's not 50% of the output.

The culprit here appears to be the RAND_RANGE macro in ext/standard/php_rand.h, which handles taking a random value and multiplying it out into the expected range, but does so in a somewhat non-obvious way. Not being a cryptographic or randomness expert, I'm not really sure if there's a sensible solution to this -- and we probably can't change it now anyway, given that stability of random number sequences given a seed is expected -- so I'm going to morph this into a doc bug for now and document that mt_rand() does weird things with bounds beyond 2^32.
 [2011-08-28 04:35 UTC] aharvey@php.net
Automatic comment from SVN on behalf of aharvey
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=315633
Log: Fix doc bug #55519 (mt_rand only generetes even 64 bits numbers) by adding a
cautionary note to the mt_rand() documentation.
 [2011-08-28 04:36 UTC] aharvey@php.net
-Status: Assigned +Status: Closed
 [2011-08-28 04:36 UTC] aharvey@php.net
This bug has been fixed in the documentation's XML sources. Since the
online and downloadable versions of the documentation need some time
to get updated, we would like to ask you to be a bit patient.

Thank you for the report, and for helping us make our documentation better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Sep 12 04:01:28 2024 UTC