doc-en icon indicating copy to clipboard operation
doc-en copied to clipboard

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

Open ausi opened this issue 1 year ago • 2 comments
trafficstars

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 avatar Apr 22 '24 20:04 ausi

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 avatar Apr 23 '24 18:04 TimWolla

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.

zeriyoshi avatar Apr 24 '24 07:04 zeriyoshi