Skip to content

Commit 15a1706

Browse files
committed
Add Randomizer::getFloat() implementing the y-section algorithm
The algorithm is 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
1 parent 27a0bba commit 15a1706

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

ext/random/random.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ public function nextInt(): int {}
135135

136136
public function nextFloat(): float {}
137137

138+
public function getFloat(float $min, float $max): float {}
139+
138140
public function getInt(int $min, int $max): int {}
139141

140142
public function getBytes(int $length): string {}

ext/random/random_arginfo.h

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/random/randomizer.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,80 @@ PHP_METHOD(Random_Randomizer, nextFloat)
150150
}
151151
/* }}} */
152152

153+
static double getFloat_gamma_low(double x)
154+
{
155+
return x - nextafter(x, -DBL_MAX);
156+
}
157+
158+
static double getFloat_gamma_high(double x)
159+
{
160+
return nextafter(x, DBL_MAX) - x;
161+
}
162+
163+
static double getFloat_gamma(double x, double y)
164+
{
165+
double high = getFloat_gamma_high(x);
166+
double low = getFloat_gamma_low(y);
167+
168+
return high > low ? high : low;
169+
}
170+
171+
static uint64_t getFloat_ceilint(double a, double b, double g)
172+
{
173+
double s = b / g - a / g;
174+
double e;
175+
176+
if (fabs(a) <= fabs(b)) {
177+
e = -a / g - (s - b / g);
178+
} else {
179+
e = b / g - (s + a / g);
180+
}
181+
182+
double si = ceil(s);
183+
184+
return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0);
185+
}
186+
187+
/* {{{ Generates a random float within [min, max).
188+
*
189+
* The algorithm used is the γ-section algorithm as published in:
190+
*
191+
* Drawing Random Floating-Point Numbers from an Interval. Frédéric
192+
* Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.
193+
* https://doi.org/10.1145/3503512
194+
*/
195+
PHP_METHOD(Random_Randomizer, getFloat)
196+
{
197+
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
198+
double min, max;
199+
200+
ZEND_PARSE_PARAMETERS_START(2, 2)
201+
Z_PARAM_DOUBLE(min)
202+
Z_PARAM_DOUBLE(max)
203+
ZEND_PARSE_PARAMETERS_END();
204+
205+
#ifndef __STDC_IEC_559__
206+
zend_throw_exception(random_ce_Random_RandomException, "The nextFloat() method requires the underlying 'double' representation to be IEEE-754.", 0);
207+
RETURN_THROWS();
208+
#endif
209+
210+
if (UNEXPECTED(max < min)) {
211+
zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
212+
RETURN_THROWS();
213+
}
214+
215+
double g = getFloat_gamma(min, max);
216+
uint64_t hi = getFloat_ceilint(min, max, g);
217+
uint64_t k = randomizer->algo->range(randomizer->status, 1, hi);
218+
219+
if (fabs(min) <= fabs(max)) {
220+
RETURN_DOUBLE(k == hi ? min : max - k * g);
221+
} else {
222+
RETURN_DOUBLE(min + (k - 1) * g);
223+
}
224+
}
225+
/* }}} */
226+
153227
/* {{{ Generate random number in range */
154228
PHP_METHOD(Random_Randomizer, getInt)
155229
{

0 commit comments

Comments
 (0)