diff --git a/NEWS b/NEWS index 6b45dc03c3117..ac32c41274f8c 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,7 @@ PHP NEWS - Random: . Added Randomizer::getBytesFromString(). (Joshua Rüsweg) + . Added Randomizer::nextFloat(), ::getFloat(), and IntervalBoundary. (timwolla) - Reflection: . Fix GH-9470 (ReflectionMethod constructor should not find private parent diff --git a/UPGRADING b/UPGRADING index fb40184c7d54d..7a6fdac636bb1 100644 --- a/UPGRADING +++ b/UPGRADING @@ -66,6 +66,8 @@ PHP 8.3 UPGRADE NOTES - Random: . Added Randomizer::getBytesFromString(). RFC: https://wiki.php.net/rfc/randomizer_additions + . Added Randomizer::nextFloat(), ::getFloat(), and IntervalBoundary. + RFC: https://wiki.php.net/rfc/randomizer_additions - Sockets: . Added socket_atmark to checks if the socket is OOB marked. diff --git a/ext/random/config.m4 b/ext/random/config.m4 index a8e6d5a568991..8ed67b9fddaca 100644 --- a/ext/random/config.m4 +++ b/ext/random/config.m4 @@ -25,6 +25,7 @@ PHP_NEW_EXTENSION(random, engine_xoshiro256starstar.c \ engine_secure.c \ engine_user.c \ + gammasection.c \ randomizer.c, no,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_INSTALL_HEADERS([ext/random], [php_random.h]) diff --git a/ext/random/config.w32 b/ext/random/config.w32 index bfbd153c1680d..e66e094039d19 100644 --- a/ext/random/config.w32 +++ b/ext/random/config.w32 @@ -1,4 +1,4 @@ EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); PHP_RANDOM="yes"; -ADD_SOURCES(configure_module_dirname, "engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c randomizer.c", "random"); +ADD_SOURCES(configure_module_dirname, "engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c", "random"); PHP_INSTALL_HEADERS("ext/random", "php_random.h"); diff --git a/ext/random/gammasection.c b/ext/random/gammasection.c new file mode 100644 index 0000000000000..fb0c2cd3bf94d --- /dev/null +++ b/ext/random/gammasection.c @@ -0,0 +1,115 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Tim Düsterhus | + | | + | Based on code from: Frédéric Goualard | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_random.h" +#include + +/* This file implements the γ-section algorithm as published in: + * + * Drawing Random Floating-Point Numbers from an Interval. Frédéric + * Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022. + * https://doi.org/10.1145/3503512 + */ + +static double gamma_low(double x) +{ + return x - nextafter(x, -DBL_MAX); +} + +static double gamma_high(double x) +{ + return nextafter(x, DBL_MAX) - x; +} + +static double gamma_max(double x, double y) +{ + return (fabs(x) > fabs(y)) ? gamma_high(x) : gamma_low(y); +} + +static uint64_t ceilint(double a, double b, double g) +{ + double s = b / g - a / g; + double e; + + if (fabs(a) <= fabs(b)) { + e = -a / g - (s - b / g); + } else { + e = b / g - (s + a / g); + } + + double si = ceil(s); + + return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0); +} + +PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max) +{ + double g = gamma_max(min, max); + uint64_t hi = ceilint(min, max, g); + uint64_t k = 1 + php_random_range64(algo, status, hi - 1); /* [1, hi] */ + + if (fabs(min) <= fabs(max)) { + return k == hi ? min : max - k * g; + } else { + return min + (k - 1) * g; + } +} + +PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max) +{ + double g = gamma_max(min, max); + uint64_t hi = ceilint(min, max, g); + uint64_t k = php_random_range64(algo, status, hi); /* [0, hi] */ + + if (fabs(min) <= fabs(max)) { + return k == hi ? min : max - k * g; + } else { + return k == hi ? max : min + k * g; + } +} + +PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max) +{ + double g = gamma_max(min, max); + uint64_t hi = ceilint(min, max, g); + uint64_t k = php_random_range64(algo, status, hi - 1); /* [0, hi - 1] */ + + if (fabs(min) <= fabs(max)) { + return max - k * g; + } else { + return k == (hi - 1) ? max : min + (k + 1) * g; + } +} + +PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max) +{ + double g = gamma_max(min, max); + uint64_t hi = ceilint(min, max, g); + uint64_t k = 1 + php_random_range64(algo, status, hi - 2); /* [1, hi - 1] */ + + if (fabs(min) <= fabs(max)) { + return max - k * g; + } else { + return min + k * g; + } +} diff --git a/ext/random/php_random.h b/ext/random/php_random.h index a4665b5d10aca..52a6b787edde5 100644 --- a/ext/random/php_random.h +++ b/ext/random/php_random.h @@ -270,8 +270,11 @@ extern PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64; extern PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937; extern PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar; extern PHPAPI zend_class_entry *random_ce_Random_Engine_Secure; + extern PHPAPI zend_class_entry *random_ce_Random_Randomizer; +extern PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary; + static inline php_random_engine *php_random_engine_from_obj(zend_object *object) { return (php_random_engine *)((char *)(object) - XtOffsetOf(php_random_engine, std)); } @@ -290,6 +293,8 @@ PHPAPI void php_random_status_free(php_random_status *status, const bool persist PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo); PHPAPI void php_random_engine_common_free_object(zend_object *object); PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object); +PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax); +PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax); PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, zend_long min, zend_long max); PHPAPI const php_random_algo *php_random_default_algo(void); PHPAPI php_random_status *php_random_default_status(void); @@ -306,6 +311,11 @@ PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgon PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state); PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state); +PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max); +PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max); +PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max); +PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max); + extern zend_module_entry random_module_entry; # define phpext_random_ptr &random_module_entry diff --git a/ext/random/random.c b/ext/random/random.c index 5f6ae0c720681..e35b3bbba47eb 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -26,6 +26,7 @@ #include "php.h" +#include "Zend/zend_enum.h" #include "Zend/zend_exceptions.h" #include "php_random.h" @@ -76,6 +77,8 @@ PHPAPI zend_class_entry *random_ce_Random_Engine_Secure; PHPAPI zend_class_entry *random_ce_Random_Randomizer; +PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary; + PHPAPI zend_class_entry *random_ce_Random_RandomError; PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError; PHPAPI zend_class_entry *random_ce_Random_RandomException; @@ -86,7 +89,7 @@ static zend_object_handlers random_engine_xoshiro256starstar_object_handlers; static zend_object_handlers random_engine_secure_object_handlers; static zend_object_handlers random_randomizer_object_handlers; -static inline uint32_t rand_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax) +PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax) { uint32_t result, limit; size_t total_size = 0; @@ -142,7 +145,7 @@ static inline uint32_t rand_range32(const php_random_algo *algo, php_random_stat return result % umax; } -static inline uint64_t rand_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax) +PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax) { uint64_t result, limit; size_t total_size = 0; @@ -310,10 +313,10 @@ PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status zend_ulong umax = (zend_ulong) max - (zend_ulong) min; if (umax > UINT32_MAX) { - return (zend_long) (rand_range64(algo, status, umax) + min); + return (zend_long) (php_random_range64(algo, status, umax) + min); } - return (zend_long) (rand_range32(algo, status, umax) + min); + return (zend_long) (php_random_range32(algo, status, umax) + min); } /* }}} */ @@ -896,6 +899,9 @@ PHP_MINIT_FUNCTION(random) random_randomizer_object_handlers.free_obj = randomizer_free_obj; random_randomizer_object_handlers.clone_obj = NULL; + /* Random\IntervalBoundary */ + random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary(); + register_random_symbols(module_number); return SUCCESS; diff --git a/ext/random/random.stub.php b/ext/random/random.stub.php index 69049a837b2c2..e87aec3a27ced 100644 --- a/ext/random/random.stub.php +++ b/ext/random/random.stub.php @@ -133,6 +133,10 @@ public function __construct(?Engine $engine = null) {} public function nextInt(): int {} + public function nextFloat(): float {} + + public function getFloat(float $min, float $max, IntervalBoundary $boundary = IntervalBoundary::ClosedOpen): float {} + public function getInt(int $min, int $max): int {} public function getBytes(int $length): string {} @@ -150,6 +154,13 @@ public function __serialize(): array {} public function __unserialize(array $data): void {} } + enum IntervalBoundary { + case ClosedOpen; + case ClosedClosed; + case OpenClosed; + case OpenOpen; + } + /** * @strict-properties */ diff --git a/ext/random/random_arginfo.h b/ext/random/random_arginfo.h index 1da1b8576b196..5fc34c95aee63 100644 --- a/ext/random/random_arginfo.h +++ b/ext/random/random_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a4226bc7838eba98c5a935b279f681a7d083c0b2 */ + * Stub hash: 7b9594d2eadb778ecec34114b67f2d0ae8bbb58a */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0) ZEND_END_ARG_INFO() @@ -90,6 +90,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_Random_Randomizer_nextInt arginfo_mt_getrandmax +#define arginfo_class_Random_Randomizer_nextFloat arginfo_lcg_value + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Random_Randomizer_getFloat, 0, 2, IS_DOUBLE, 0) + ZEND_ARG_TYPE_INFO(0, min, IS_DOUBLE, 0) + ZEND_ARG_TYPE_INFO(0, max, IS_DOUBLE, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, boundary, Random\\IntervalBoundary, 0, "Random\\IntervalBoundary::ClosedOpen") +ZEND_END_ARG_INFO() + #define arginfo_class_Random_Randomizer_getInt arginfo_random_int #define arginfo_class_Random_Randomizer_getBytes arginfo_random_bytes @@ -136,6 +144,8 @@ ZEND_METHOD(Random_Engine_Xoshiro256StarStar, jump); ZEND_METHOD(Random_Engine_Xoshiro256StarStar, jumpLong); ZEND_METHOD(Random_Randomizer, __construct); ZEND_METHOD(Random_Randomizer, nextInt); +ZEND_METHOD(Random_Randomizer, nextFloat); +ZEND_METHOD(Random_Randomizer, getFloat); ZEND_METHOD(Random_Randomizer, getInt); ZEND_METHOD(Random_Randomizer, getBytes); ZEND_METHOD(Random_Randomizer, getBytesFromString); @@ -213,6 +223,8 @@ static const zend_function_entry class_Random_CryptoSafeEngine_methods[] = { static const zend_function_entry class_Random_Randomizer_methods[] = { ZEND_ME(Random_Randomizer, __construct, arginfo_class_Random_Randomizer___construct, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, nextInt, arginfo_class_Random_Randomizer_nextInt, ZEND_ACC_PUBLIC) + ZEND_ME(Random_Randomizer, nextFloat, arginfo_class_Random_Randomizer_nextFloat, ZEND_ACC_PUBLIC) + ZEND_ME(Random_Randomizer, getFloat, arginfo_class_Random_Randomizer_getFloat, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getInt, arginfo_class_Random_Randomizer_getInt, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getBytes, arginfo_class_Random_Randomizer_getBytes, ZEND_ACC_PUBLIC) ZEND_ME(Random_Randomizer, getBytesFromString, arginfo_class_Random_Randomizer_getBytesFromString, ZEND_ACC_PUBLIC) @@ -225,6 +237,11 @@ static const zend_function_entry class_Random_Randomizer_methods[] = { }; +static const zend_function_entry class_Random_IntervalBoundary_methods[] = { + ZEND_FE_END +}; + + static const zend_function_entry class_Random_RandomError_methods[] = { ZEND_FE_END }; @@ -332,6 +349,21 @@ static zend_class_entry *register_class_Random_Randomizer(void) return class_entry; } +static zend_class_entry *register_class_Random_IntervalBoundary(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Random\\IntervalBoundary", IS_UNDEF, class_Random_IntervalBoundary_methods); + + zend_enum_add_case_cstr(class_entry, "ClosedOpen", NULL); + + zend_enum_add_case_cstr(class_entry, "ClosedClosed", NULL); + + zend_enum_add_case_cstr(class_entry, "OpenClosed", NULL); + + zend_enum_add_case_cstr(class_entry, "OpenOpen", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Random_RandomError(zend_class_entry *class_entry_Error) { zend_class_entry ce, *class_entry; diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index a95e6b0fdd8a8..0a801e35c74c6 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -24,6 +24,7 @@ #include "ext/standard/php_array.h" #include "ext/standard/php_string.h" +#include "Zend/zend_enum.h" #include "Zend/zend_exceptions.h" static inline void randomizer_common_init(php_random_randomizer *randomizer, zend_object *engine_object) { @@ -88,6 +89,114 @@ PHP_METHOD(Random_Randomizer, __construct) } /* }}} */ +/* {{{ Generate a float in [0, 1) */ +PHP_METHOD(Random_Randomizer, nextFloat) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + uint64_t result; + size_t total_size; + + ZEND_PARSE_PARAMETERS_NONE(); + + result = 0; + total_size = 0; + do { + uint64_t r = randomizer->algo->generate(randomizer->status); + result = result | (r << (total_size * 8)); + total_size += randomizer->status->last_generated_size; + if (EG(exception)) { + RETURN_THROWS(); + } + } while (total_size < sizeof(uint64_t)); + + /* A double has 53 bits of precision, thus we must not + * use the full 64 bits of the uint64_t, because we would + * introduce a bias / rounding error. + */ +#if DBL_MANT_DIG != 53 +# error "Random_Randomizer::nextFloat(): Requires DBL_MANT_DIG == 53 to work." +#endif + const double step_size = 1.0 / (1ULL << 53); + + /* Use the upper 53 bits, because some engine's lower bits + * are of lower quality. + */ + result = (result >> 11); + + RETURN_DOUBLE(step_size * result); +} +/* }}} */ + +/* {{{ Generates a random float within a configurable interval. + * + * This method uses the γ-section algorithm by Frédéric Goualard. + */ +PHP_METHOD(Random_Randomizer, getFloat) +{ + php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); + double min, max; + zend_object *bounds = NULL; + int bounds_type = 'C' + sizeof("ClosedOpen") - 1; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_DOUBLE(min) + Z_PARAM_DOUBLE(max) + Z_PARAM_OPTIONAL + Z_PARAM_OBJ_OF_CLASS(bounds, random_ce_Random_IntervalBoundary); + ZEND_PARSE_PARAMETERS_END(); + + if (!zend_finite(min)) { + zend_argument_value_error(1, "must be finite"); + RETURN_THROWS(); + } + + if (!zend_finite(max)) { + zend_argument_value_error(2, "must be finite"); + RETURN_THROWS(); + } + + if (bounds) { + zval *case_name = zend_enum_fetch_case_name(bounds); + zend_string *bounds_name = Z_STR_P(case_name); + + bounds_type = ZSTR_VAL(bounds_name)[0] + ZSTR_LEN(bounds_name); + } + + switch (bounds_type) { + case 'C' + sizeof("ClosedOpen") - 1: + if (UNEXPECTED(max <= min)) { + zend_argument_value_error(2, "must be greater than argument #1 ($min)"); + RETURN_THROWS(); + } + + RETURN_DOUBLE(php_random_gammasection_closed_open(randomizer->algo, randomizer->status, min, max)); + case 'C' + sizeof("ClosedClosed") - 1: + if (UNEXPECTED(max < min)) { + zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)"); + RETURN_THROWS(); + } + + RETURN_DOUBLE(php_random_gammasection_closed_closed(randomizer->algo, randomizer->status, min, max)); + case 'O' + sizeof("OpenClosed") - 1: + if (UNEXPECTED(max <= min)) { + zend_argument_value_error(2, "must be greater than argument #1 ($min)"); + RETURN_THROWS(); + } + + RETURN_DOUBLE(php_random_gammasection_open_closed(randomizer->algo, randomizer->status, min, max)); + case 'O' + sizeof("OpenOpen") - 1: + if (UNEXPECTED(max <= min)) { + zend_argument_value_error(2, "must be greater than argument #1 ($min)"); + RETURN_THROWS(); + } + + RETURN_DOUBLE(php_random_gammasection_open_open(randomizer->algo, randomizer->status, min, max)); + default: + ZEND_UNREACHABLE(); + } +} +/* }}} */ + /* {{{ Generate positive random number */ PHP_METHOD(Random_Randomizer, nextInt) { diff --git a/ext/random/tests/03_randomizer/methods/getFloat.phpt b/ext/random/tests/03_randomizer/methods/getFloat.phpt new file mode 100644 index 0000000000000..1fcec7f3e223a --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getFloat.phpt @@ -0,0 +1,50 @@ +--TEST-- +Random: Randomizer: getFloat(): Basic functionality +--FILE-- +getFloat(-$i, $i, IntervalBoundary::ClosedClosed); + + if ($result > $i || $result < -$i) { + die("failure: out of range at {$i}"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/methods/getFloat_error.phpt b/ext/random/tests/03_randomizer/methods/getFloat_error.phpt new file mode 100644 index 0000000000000..1e200f2507a69 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getFloat_error.phpt @@ -0,0 +1,119 @@ +--TEST-- +Random: Randomizer: getFloat(): Parameters are correctly validated +--FILE-- +name, PHP_EOL; + + try { + var_dump(randomizer()->getFloat(NAN, 0.0, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(INF, 0.0, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(-INF, 0.0, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(0.0, NAN, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(0.0, INF, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(0.0, -INF, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(0.0, -0.1, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + var_dump(randomizer()->getFloat(0.0, 0.0, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } + + try { + // Both values round to the same float. + var_dump(randomizer()->getFloat(100_000_000_000_000_000.0, 100_000_000_000_000_000.1, $boundary)); + } catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +ClosedClosed +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than or equal to argument #1 ($min) +float(0) +float(1.0E+17) +ClosedOpen +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +OpenClosed +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +OpenOpen +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #1 ($min) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be finite +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) +Random\Randomizer::getFloat(): Argument #2 ($max) must be greater than argument #1 ($min) diff --git a/ext/random/tests/03_randomizer/methods/nextFloat.phpt b/ext/random/tests/03_randomizer/methods/nextFloat.phpt new file mode 100644 index 0000000000000..4a583d00e78d9 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/nextFloat.phpt @@ -0,0 +1,49 @@ +--TEST-- +Random: Randomizer: nextFloat(): Basic functionality +--FILE-- +nextFloat(); + + if ($result >= 1 || $result < 0) { + die("failure: out of range at {$i}"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success