Hi Internals.
Congratulations on the release of PHP 8.2.
I just recently upgraded production PHP from 7.4 to 8.1 :)
Now that my work is done, I was thinking about a proposal for a sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative to lcg_value()
.
Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easier random_int()
and
random_bytes()
functions for ints and strings.
The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.
So, why not add a random_float(): float
function? This function, like the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This behavior
is Closed
Closed
.
Opinions are welcome.
Regards,
Go Kudo
returns a value between 0.0 and 1.0.
wouldn't it be better to follow random_int(int $min, int $max) design? eg
random_float(float $min, float $max): float
Hi Internals.
Congratulations on the release of PHP 8.2.
I just recently upgraded production PHP from 7.4 to 8.1 :)Now that my work is done, I was thinking about a proposal for a sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative tolcg_value()
.Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easierrandom_int()
and
random_bytes()
functions for ints and strings.The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.So, why not add a
random_float(): float
function? This function, like the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This behavior
isClosed
Closed
.Opinions are welcome.
Regards,
Go Kudo
btw while we're on the topic, does anyone know if this function gives
biased results/is-safe or not? i honestly don't know:
function random_float(float $min, float $max): float
{
if ($min > $max) throw new \InvalidArgumentException("min must be
less than max");
if ($min === $max) return $min;
return $min + random_int(0, PHP_INT_MAX) / PHP_INT_MAX
* ($max - $min);
}
returns a value between 0.0 and 1.0.
wouldn't it be better to follow random_int(int $min, int $max) design? eg
random_float(float $min, float $max): floatHi Internals.
Congratulations on the release of PHP 8.2.
I just recently upgraded production PHP from 7.4 to 8.1 :)Now that my work is done, I was thinking about a proposal for a sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative tolcg_value()
.Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easierrandom_int()
and
random_bytes()
functions for ints and strings.The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.So, why not add a
random_float(): float
function? This function, like the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This behavior
isClosed
Closed
.Opinions are welcome.
Regards,
Go Kudo
2022年12月20日(火) 17:15 Hans Henrik Bergan divinity76@gmail.com:
btw while we're on the topic, does anyone know if this function gives
biased results/is-safe or not? i honestly don't know:
function random_float(float $min, float $max): float
{
if ($min > $max) throw new \InvalidArgumentException("min must be
less than max");
if ($min === $max) return $min;
return $min + random_int(0, PHP_INT_MAX) /PHP_INT_MAX
* ($max - $min);
}On Tue, 20 Dec 2022 at 09:06, Hans Henrik Bergan divinity76@gmail.com
wrote:returns a value between 0.0 and 1.0.
wouldn't it be better to follow random_int(int $min, int $max) design? eg
random_float(float $min, float $max): floatHi Internals.
Congratulations on the release of PHP 8.2.
I just recently upgraded production PHP from 7.4 to 8.1 :)Now that my work is done, I was thinking about a proposal for a
sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative to
lcg_value()
.Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easierrandom_int()
and
random_bytes()
functions for ints and strings.The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.So, why not add a
random_float(): float
function? This function,
like the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This
behavior
isClosed
Closed
.Opinions are welcome.
Regards,
Go Kudo
Hi.
The dangers of generating floating-point random numbers from PHP integer
values are explained in detail in Tim and Joshua's RFC.
https://wiki.php.net/rfc/randomizer_additions
I missed it at first too, and was negative about adding
Randomizer::getFloat()
...
In general, I think this problem is less noticeable and the absence of the
random_float() function increases the likelihood of incorrect
implementations in userland.
Regards,
Go Kudo
2022年12月20日(火) 18:14 Go Kudo g-kudo@colopl.co.jp:
2022年12月20日(火) 17:15 Hans Henrik Bergan divinity76@gmail.com:
btw while we're on the topic, does anyone know if this function gives
biased results/is-safe or not? i honestly don't know:
function random_float(float $min, float $max): float
{
if ($min > $max) throw new \InvalidArgumentException("min must be
less than max");
if ($min === $max) return $min;
return $min + random_int(0, PHP_INT_MAX) /PHP_INT_MAX
* ($max -
$min);
}On Tue, 20 Dec 2022 at 09:06, Hans Henrik Bergan divinity76@gmail.com
wrote:returns a value between 0.0 and 1.0.
wouldn't it be better to follow random_int(int $min, int $max) design?
eg
random_float(float $min, float $max): floatHi Internals.
Congratulations on the release of PHP 8.2.
I just recently upgraded production PHP from 7.4 to 8.1 :)Now that my work is done, I was thinking about a proposal for a
sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative to
lcg_value()
.Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easierrandom_int()
and
random_bytes()
functions for ints and strings.The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.So, why not add a
random_float(): float
function? This function,
like the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This
behavior
isClosed
Closed
.Opinions are welcome.
Regards,
Go KudoHi.
The dangers of generating floating-point random numbers from PHP integer
values are explained in detail in Tim and Joshua's RFC.https://wiki.php.net/rfc/randomizer_additions
I missed it at first too, and was negative about adding
Randomizer::getFloat()
...
In general, I think this problem is less noticeable and the absence of the
random_float() function increases the likelihood of incorrect
implementations in userland.Regards,
Go Kudo
In the PHP-8.3 current development branch, you can already polyfill as
follows However, it is complicated.
<?php
function random_float(float $min = 0.0, float $max = 1.0,
\Random\IntervalBoundary $intervalBoundary =
\Random\IntervalBoundary::ClosedClosed): float {
return (new \Random\Randomizer())->getFloat($min, $max,
$intervalBoundary);
}
Hi
btw while we're on the topic, does anyone know if this function gives
biased results/is-safe or not? i honestly don't know:
function random_float(float $min, float $max): float
{
if ($min > $max) throw new \InvalidArgumentException("min must be
less than max");
if ($min === $max) return $min;
return $min + random_int(0, PHP_INT_MAX) /PHP_INT_MAX
* ($max - $min);
}
To give a clear answer on this one:
For a full evaluation the reader lacks information: What is this
function supposed to do? For floats, the definition of the boundaries
matter (that's why it's a parameter for Randomizer::getFloat()). Your
function is implemented on the closed interval [0, 1] (i.e. both
including 0 and 1). As mentioned in my email a few minutes ago, I
consider the ClosedClosed behavior to not be the most useful in the
general case, but in your case it might be the correct behavior.
Nonetheless, the function is definitely broken:
- A IEEE 754 double precision float as used by PHP only has 53 Bits of
precision, but you generate a random 64 Bit integer. Thus rounding will
occur and might cause some values to be returned more often than other.
To improve the function you should only generate 53 Bits of randomness
(i.e. use 253 as the divisor and either 253-1 or 2**53 as the dividend).
- Using the (min + rand * (max - min)) construction will generate
biased results (see also my other email).
PHP 8.3's Randomizer::getFloat() method uses the γ-section algorithm
proposed by Prof. Goualard to generate unbiased results for arbitrary
ranges. This algorithm cannot reasonably be implemented in userland,
because the necessary "building blocks" (obtaining the next/previous
float for a given float) are not available for userland code. The best
you can do in PHP 8.2 userland is what Randomizer::nextFloat() does.
TLDR: Yes, the function is broken.
Best regards
Tim Düsterhus
Hi
Now that my work is done, I was thinking about a proposal for a sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative tolcg_value()
.Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easierrandom_int()
and
random_bytes()
functions for ints and strings.The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.So, why not add a
random_float(): float
function? This function, like the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This behavior
isClosed
Closed
.Opinions are welcome.
I'm not convinced that a random_float() function is a good addition.
Especially not with the proposed behavior:
-
Using the (0, 1, ClosedClosed) behavior does neither match
Randomizer::nextFloat(), nor Randomizer::getFloat(). -
I consider the ClosedOpen behavior to be the "correct" default,
because the interval can then be cleanly split into equally sized
subintervals.
But even when random_float() would work like Randomizer::nextFloat()
(i.e. (0, 1, ClosedOpen)), I would not consider this a good addition:
Users would likely attempt to scale the returned value to arbitrary
ranges using the ($min + random_float() * ($max - $min))
construction,
instead of using Randomizer::getFloat().
As per Drawing Random Floating-Point Numbers from an Interval. Frédéric
Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022 this construction
is unsafe, because is is both biased and also may return values outside
the expected interval.
So random_float() would rather need to behave like
Randomizer::getFloat() instead of Randomizer::nextFloat() and then it
would not be much simpler than using the Randomizer class directly. I
further expect that needing to generate random floats is much rarer than
needing to generate random integers or random bytes (for tokens), thus
having convenience functions for those two, but not floats is acceptable
and keeps the API surface simple, making it easier to document all the
gotchas. Users can add convenience wrappers themselves.
Best regards
Tim Düsterhus
2022年12月29日(木) 22:25 Tim Düsterhus tim@bastelstu.be:
Hi
Now that my work is done, I was thinking about a proposal for a
sunsetting
of existing functions for PHP 8.3 based on the features introduced in
ext-random, and I realized that there is no alternative tolcg_value()
.Essentially, this could be completely replaced by
Random\Randomizer::getFloat(), but there are easierrandom_int()
and
random_bytes()
functions for ints and strings.The Randomizer may be overkill and complicated for PHP use cases where
random number reproducibility is not required in many cases.So, why not add a
random_float(): float
function? This function, like
the
others, uses CSPRNG and returns a value between 0.0 and 1.0. This
behavior
isClosed
Closed
.Opinions are welcome.
I'm not convinced that a random_float() function is a good addition.
Especially not with the proposed behavior:
Using the (0, 1, ClosedClosed) behavior does neither match
Randomizer::nextFloat(), nor Randomizer::getFloat().I consider the ClosedOpen behavior to be the "correct" default,
because the interval can then be cleanly split into equally sized
subintervals.
But even when random_float() would work like Randomizer::nextFloat()
(i.e. (0, 1, ClosedOpen)), I would not consider this a good addition:Users would likely attempt to scale the returned value to arbitrary
ranges using the($min + random_float() * ($max - $min))
construction,
instead of using Randomizer::getFloat().As per Drawing Random Floating-Point Numbers from an Interval. Frédéric
Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022 this construction
is unsafe, because is is both biased and also may return values outside
the expected interval.
So random_float() would rather need to behave like
Randomizer::getFloat() instead of Randomizer::nextFloat() and then it
would not be much simpler than using the Randomizer class directly. I
further expect that needing to generate random floats is much rarer than
needing to generate random integers or random bytes (for tokens), thus
having convenience functions for those two, but not floats is acceptable
and keeps the API surface simple, making it easier to document all the
gotchas. Users can add convenience wrappers themselves.Best regards
Tim Düsterhus
Hi Tim
Thank you for the clear explanation. I generally understood the intent. And
once again I recognized that floating point random numbers are difficult to
handle.
I agree that the addition of the random_float() function is inappropriate.
However, with the goal of deprecating it in PHP 8.3 and deprecating RNGs
with virtual machine dependent state in PHP 9.x, I think we need to provide
a useful replacement for the lcg_value()
function.
Do you have any good ideas on this?
Most likely, since the random numbers generated by lcg_value()
are of low
quality and do not appear to be used very often, one idea would be to
simply remove it. However, I believe that the absence of an alternative
function is likely to generate negative opinions about its elimination.
Regards,
Go Kudo