Skip to content

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

Open
@ausi

Description

@ausi

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)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions