@@ -150,6 +150,80 @@ PHP_METHOD(Random_Randomizer, nextFloat)
150
150
}
151
151
/* }}} */
152
152
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
+
153
227
/* {{{ Generate random number in range */
154
228
PHP_METHOD (Random_Randomizer , getInt )
155
229
{
0 commit comments