PHP中0到1之间的随机浮点数

47

如何在PHP中生成0到1之间的随机浮点数?

我正在寻找PHP中等同于Java的Math.random()的函数。


1
rand() 文档中有一些示例。 - Michael Berkowski
那不会返回一个整数吗? - Goaler444
1
Goaler 作为程序员,您的工作是使用算术操作将 int 值转换成 float 值。文档中有详细说明。@dystroy 在此为您提供了复制内容,以方便参考。 - Michael Berkowski
好的,谢谢 =) 对于任何不便我表示抱歉(我更习惯使用Java) - Goaler444
2
我不明白为什么这个问题被关闭了。在我看来,它似乎是完全合法的。我浏览了文档,但没有找到答案。提名重新开放。 - Erick Robertson
请参考 https://dev59.com/pc8D0IgBFxS5KdRjkOQA#14155720 和第一条评论。 - caw
13个回答

0

大多数答案都使用mt_rand。然而,mt_getrandmax()通常只返回2147483647。这意味着您只有31位信息,而双精度浮点数具有52位的尾数,这意味着在0和1之间的数字至少有2^53的密度。

这种更复杂的方法将为您提供更好的分布:

function rand_754_01() {
    // Generate 64 random bits (8 bytes)
    $entropy = openssl_random_pseudo_bytes(8);
    // Create a string of 12 '0' bits and 52 '1' bits. 
    $x = 0x000FFFFFFFFFFFFF;
    $first12 = pack("Q", $x);
    // Set the first 12 bits to 0 in the random string. 
    $y = $entropy & $first12;
    // Now set the first 12 bits to be 0[exponent], where exponent is randomly chosen between 1 and 1022. 
    // Here $e has a probability of 0.5 to be 1022, 0.25 to be 1021, etc. 
    $e = 1022;     
    while($e > 1) {   
        if(mt_rand(0,1) == 0) {
            break;
        } else {
            --$e;
        }
    }
    // Pack the exponent properly (add four '0' bits behind it and 49 more in front)
    $z = "\0\0\0\0\0\0" . pack("S", $e << 4);
    // Now convert to a double. 
    return unpack("d", $y | $z)[1];          
}

请注意,上述代码仅适用于具有小端字节顺序和英特尔风格IEEE754表示的64位机器。(兼容x64的计算机将具备此功能)。不幸的是,PHP不允许超过int32大小边界进行位移,因此您必须为大端编写单独的函数。
您应该替换此行:
    $z = "\0\0\0\0\0\0" . pack("S", $e << 4);

与其大端兄弟:

    $z = pack("S", $e << 4) .  "\0\0\0\0\0\0";

只有在函数被调用大量次数时才会有明显的差异:10^9或更多。

测试是否有效

很明显,尾数遵循一个漂亮的均匀分布近似,但是一个大量这样的分布之和(每个分布具有累计减半的机会和振幅)是均匀的,这一点不太明显。

正在运行:

function randomNumbers() {
    $f = 0.0;
    for($i = 0; $i < 1000000; ++$i) {
        $f += \math::rand_754_01();
    }
    echo $f / 1000000;
}

生成一个输出值为0.49999928273099(或接近0.5的类似数字)。


有一种更简单的方法可以生成1到2之间的随机浮点数。你只需要减去1即可: $a = random_bytes(8); $a[0] = "\x3f"; $a[1] = $a[1] | "\xf0"; return unpack('E', $a)[1] - 1.0; - Nommyde
@Nommyde 浮点精度取决于值的绝对大小。通过减法,您会失去(一定量的)精度。在1.5和2之间的值只有1位,但非常接近1的值将失去大量精度。 我认为我的答案允许所有非规范化值成为可能,或者2^52 *(2^10-1)个值,约占所有可能浮点数的1/4,而该方法仅具有2^52个可能值,仅占所有可能浮点数的1/1024。 (读者的练习:找到一个由其中一个方法生成而不是另一个方法的数字的示例。) - aphid

0

我在PHP.net上找到了答案。

<?php
function randomFloat($min = 0, $max = 1) {
    return $min + mt_rand() / mt_getrandmax() * ($max - $min);
}

var_dump(randomFloat());
var_dump(randomFloat(2, 20));
?>


float(0.91601131712832)
float(16.511210331931)

所以你可以这样做

randomFloat(0,1);

或者简单一点

mt_rand() / mt_getrandmax() * 1;

-3

这个怎么样:

echo (float)('0.' . rand(0,99999));

可能会很好地工作...希望能对你有所帮助。


1
-1 这不会提供0到1之间的随机分布。考虑当随机数返回24并变成0.24时。实际上,我认为你要找的是0.000024。另一个问题是,一半的结果将在0.1和0.2之间,因为rand将在100000和199999之间。 - Erick Robertson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接