Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Randomizer::getFloat() unexpected results in extreme edge cases #3339

Open
ausi opened this issue Apr 22, 2024 · 2 comments
Open

Randomizer::getFloat() unexpected results in extreme edge cases #3339

ausi opened this issue Apr 22, 2024 · 2 comments
Labels
bug Documentation contains incorrect information Extension: random Status: Verified

Comments

@ausi
Copy link

ausi commented Apr 22, 2024

Description

While playing around with Random\Randomizer::getFloat() I noticed that it does return unexpected results in some extreme edge cases.

The following code:

<?php
$r = new \Random\Randomizer();

echo "\ninterval: (-5.0E-324, +5.0E-324)";
echo "\nexpected: float(0)\nactual:   ";
var_dump($r->getFloat(-5.0E-324, +5.0E-324, \Random\IntervalBoundary::OpenOpen));
// float(-5.0E-324)

echo "\ninterval: (0.0, 1.0E-323)";
echo "\nexpected: float(5.0E-324)\nactual:   ";
var_dump($r->getFloat(0.0, 1.0E-323, \Random\IntervalBoundary::OpenOpen));
// float(-5.0E-324)

echo "\ninterval: (5.0E-324, 1.5E-323)";
echo "\nexpected: float(1.0E-323)\nactual:   ";
var_dump($r->getFloat(5.0E-324, 1.5E-323, \Random\IntervalBoundary::OpenOpen));
// float(1.5E-323)

echo "\ninterval: (0.0, 5.0E-324]";
echo "\nexpected: float(5.0E-324)\nactual:   ";
var_dump($r->getFloat(0.0, 5.0E-324, \Random\IntervalBoundary::OpenClosed));
// float(0)

echo "\ninterval: (5.0E-324, 1.0E-323]";
echo "\nexpected: float(1.0E-323)\nactual:   ";
var_dump($r->getFloat(5.0E-324, 1.0E-323, \Random\IntervalBoundary::OpenClosed));
// float(0)

Resulted in this output:

interval: (-5.0E-324, +5.0E-324)
expected: float(0)
actual:   float(-5.0E-324)

interval: (0.0, 1.0E-323)
expected: float(5.0E-324)
actual:   float(-5.0E-324)

interval: (5.0E-324, 1.5E-323)
expected: float(1.0E-323)
actual:   float(1.5E-323)

interval: (0.0, 5.0E-324]
expected: float(5.0E-324)
actual:   float(0)

interval: (5.0E-324, 1.0E-323]
expected: float(1.0E-323)
actual:   float(0)

These are all cases where only a single float value is possible to be drawn, but the results are consistently wrong and up to two values apart from the correct result.

Slightly larger ranges also seem to be affected by this. In the following example the floats 1.0E-323 and 1.5E-323 get skipped but the floats 3.5E-323 and 4.0E-323 get included even though they are outside the boundary.

var_dump($r->getFloat(5.0E-324, 3.0E-323, \Random\IntervalBoundary::ClosedClosed));
// Should draw one of:  5.0E-324, 1.0E-323, 1.5E-323, 2.0E-323, 2.5E-323, 3.0E-323
// Insteads draws from: 5.0E-324, 2.0E-323, 2.5E-323, 3.0E-323, 3.5E-323, 4.0E-323

For reference here are the HEX representations (64bit big endian) of the used floats:

-5.0E-324: 8000000000000001
 5.0E-324: 0000000000000001
 1.0E-323: 0000000000000002
 1.5E-323: 0000000000000003
 2.0E-323: 0000000000000004
 2.5E-323: 0000000000000005
 3.0E-323: 0000000000000006
 3.5E-323: 0000000000000007
 4.0E-323: 0000000000000008

PHP Version

PHP 8.3.6

Operating System

macOS 14.4.1 (23E224)

@ausi ausi added bug Documentation contains incorrect information Status: Needs Triage labels Apr 22, 2024
@TimWolla
Copy link
Member

Thank you for the report. Underflow is intentionally left unhandled in the γ-section algorithm used by Randomizer::getFloat(). Quoting from the paper:

All our results hold in the absence of underflow. For most applications, it is standard practice to discount the possibility of underflow in error analysis. In addition, we did not consider it worthwhile to investigate further in the event of underflows as their occurrence would require the computation of very small floats for all practical formats (values of the order of 10−38 for single precision and 10−308 for double precision).

The behavior of Randomizer::getFloat() should be well-defined for inputs larger than 2^-1020 (~8.9e-308) in absolute values.

As mentioned in php/php-src#12402, I intended to document this limitation, but regretfully did not yet get around to it. I'm moving this issue to the documentation repository.

@TimWolla TimWolla transferred this issue from php/php-src Apr 23, 2024
@TimWolla TimWolla removed their assignment Apr 23, 2024
@zeriyoshi
Copy link
Contributor

Sorry for the delay in response.

As Tim has already explained, this appears to be a documentation issue.

nit: the main cause of my delayed response was my health condition, OPLL, for which I underwent a surgical procedure. I will be able to work more vigorously from now on. Stay tuned.

Thanks to @TimWolla for his proactive maintenance in my absence. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Documentation contains incorrect information Extension: random Status: Verified
Projects
None yet
Development

No branches or pull requests

4 participants