Skip to content

Commit 18f2ab8

Browse files
committed
random: Split the implementation of γ-section into its own file
1 parent 63dc454 commit 18f2ab8

File tree

5 files changed

+128
-94
lines changed

5 files changed

+128
-94
lines changed

ext/random/config.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PHP_NEW_EXTENSION(random,
2525
engine_xoshiro256starstar.c \
2626
engine_secure.c \
2727
engine_user.c \
28+
gammasection.c \
2829
randomizer.c,
2930
no,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
3031
PHP_INSTALL_HEADERS([ext/random], [php_random.h])

ext/random/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
22
PHP_RANDOM="yes";
3-
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");
3+
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");
44
PHP_INSTALL_HEADERS("ext/random", "php_random.h");

ext/random/gammasection.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Tim Düsterhus <[email protected]> |
14+
| |
15+
| Based on code from: Frédéric Goualard |
16+
+----------------------------------------------------------------------+
17+
*/
18+
19+
#ifdef HAVE_CONFIG_H
20+
# include "config.h"
21+
#endif
22+
23+
#include "php.h"
24+
#include "php_random.h"
25+
#include <math.h>
26+
27+
/* This file implements the γ-section algorithm as published in:
28+
*
29+
* Drawing Random Floating-Point Numbers from an Interval. Frédéric
30+
* Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.
31+
* https://doi.org/10.1145/3503512
32+
*/
33+
34+
static double gamma_low(double x)
35+
{
36+
return x - nextafter(x, -DBL_MAX);
37+
}
38+
39+
static double gamma_high(double x)
40+
{
41+
return nextafter(x, DBL_MAX) - x;
42+
}
43+
44+
static double gamma_max(double x, double y)
45+
{
46+
return (fabs(x) > fabs(y)) ? gamma_high(x) : gamma_low(y);
47+
}
48+
49+
static uint64_t ceilint(double a, double b, double g)
50+
{
51+
double s = b / g - a / g;
52+
double e;
53+
54+
if (fabs(a) <= fabs(b)) {
55+
e = -a / g - (s - b / g);
56+
} else {
57+
e = b / g - (s + a / g);
58+
}
59+
60+
double si = ceil(s);
61+
62+
return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0);
63+
}
64+
65+
PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max)
66+
{
67+
double g = gamma_max(min, max);
68+
uint64_t hi = ceilint(min, max, g);
69+
uint64_t k = algo->range(status, 1, hi);
70+
71+
if (fabs(min) <= fabs(max)) {
72+
return k == hi ? min : max - k * g;
73+
} else {
74+
return min + (k - 1) * g;
75+
}
76+
}
77+
78+
PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
79+
{
80+
double g = gamma_max(min, max);
81+
uint64_t hi = ceilint(min, max, g);
82+
uint64_t k = algo->range(status, 0, hi);
83+
84+
if (fabs(min) <= fabs(max)) {
85+
return k == hi ? min : max - k * g;
86+
} else {
87+
return k == hi ? max : min + k * g;
88+
}
89+
}
90+
91+
PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
92+
{
93+
double g = gamma_max(min, max);
94+
uint64_t hi = ceilint(min, max, g);
95+
uint64_t k = algo->range(status, 0, hi - 1);
96+
97+
if (fabs(min) <= fabs(max)) {
98+
return max - k * g;
99+
} else {
100+
return k == (hi - 1) ? max : min + (k + 1) * g;
101+
}
102+
}
103+
104+
PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max)
105+
{
106+
double g = gamma_max(min, max);
107+
uint64_t hi = ceilint(min, max, g);
108+
uint64_t k = algo->range(status, 1, hi - 1);
109+
110+
if (fabs(min) <= fabs(max)) {
111+
return max - k * g;
112+
} else {
113+
return min + k * g;
114+
}
115+
}

ext/random/php_random.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgon
309309
PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state);
310310
PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state);
311311

312+
PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max);
313+
PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max);
314+
PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max);
315+
PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max);
316+
312317
extern zend_module_entry random_module_entry;
313318
# define phpext_random_ptr &random_module_entry
314319

ext/random/randomizer.c

Lines changed: 6 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -130,96 +130,9 @@ PHP_METHOD(Random_Randomizer, nextFloat)
130130
}
131131
/* }}} */
132132

133-
static double getFloat_gamma_low(double x)
134-
{
135-
return x - nextafter(x, -DBL_MAX);
136-
}
137-
138-
static double getFloat_gamma_high(double x)
139-
{
140-
return nextafter(x, DBL_MAX) - x;
141-
}
142-
143-
static double getFloat_gamma(double x, double y)
144-
{
145-
return (fabs(x) > fabs(y)) ? getFloat_gamma_high(x) : getFloat_gamma_low(y);
146-
}
147-
148-
static uint64_t getFloat_ceilint(double a, double b, double g)
149-
{
150-
double s = b / g - a / g;
151-
double e;
152-
153-
if (fabs(a) <= fabs(b)) {
154-
e = -a / g - (s - b / g);
155-
} else {
156-
e = b / g - (s + a / g);
157-
}
158-
159-
double si = ceil(s);
160-
161-
return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0);
162-
}
163-
164-
static double getFloat_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max)
165-
{
166-
double g = getFloat_gamma(min, max);
167-
uint64_t hi = getFloat_ceilint(min, max, g);
168-
uint64_t k = algo->range(status, 1, hi);
169-
170-
if (fabs(min) <= fabs(max)) {
171-
return k == hi ? min : max - k * g;
172-
} else {
173-
return min + (k - 1) * g;
174-
}
175-
}
176-
177-
static double getFloat_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
178-
{
179-
double g = getFloat_gamma(min, max);
180-
uint64_t hi = getFloat_ceilint(min, max, g);
181-
uint64_t k = algo->range(status, 0, hi);
182-
183-
if (fabs(min) <= fabs(max)) {
184-
return k == hi ? min : max - k * g;
185-
} else {
186-
return k == hi ? max : min + k * g;
187-
}
188-
}
189-
190-
static double getFloat_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
191-
{
192-
double g = getFloat_gamma(min, max);
193-
uint64_t hi = getFloat_ceilint(min, max, g);
194-
uint64_t k = algo->range(status, 0, hi - 1);
195-
196-
if (fabs(min) <= fabs(max)) {
197-
return max - k * g;
198-
} else {
199-
return k == (hi - 1) ? max : min + (k + 1) * g;
200-
}
201-
}
202-
203-
static double getFloat_open_open(const php_random_algo *algo, php_random_status *status, double min, double max)
204-
{
205-
double g = getFloat_gamma(min, max);
206-
uint64_t hi = getFloat_ceilint(min, max, g);
207-
uint64_t k = algo->range(status, 1, hi - 1);
208-
209-
if (fabs(min) <= fabs(max)) {
210-
return max - k * g;
211-
} else {
212-
return min + k * g;
213-
}
214-
}
215-
216-
/* {{{ Generates a random float within [min, max).
217-
*
218-
* The algorithm used is the γ-section algorithm as published in:
133+
/* {{{ Generates a random float within a configurable interval.
219134
*
220-
* Drawing Random Floating-Point Numbers from an Interval. Frédéric
221-
* Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.
222-
* https://doi.org/10.1145/3503512
135+
* This method uses the γ-section algorithm by Frédéric Goualard.
223136
*/
224137
PHP_METHOD(Random_Randomizer, getFloat)
225138
{
@@ -251,28 +164,28 @@ PHP_METHOD(Random_Randomizer, getFloat)
251164
RETURN_THROWS();
252165
}
253166

254-
RETURN_DOUBLE(getFloat_closed_open(randomizer->algo, randomizer->status, min, max));
167+
RETURN_DOUBLE(php_random_gammasection_closed_open(randomizer->algo, randomizer->status, min, max));
255168
} else if (zend_string_equals_literal(bounds_name, "ClosedClosed")) {
256169
if (UNEXPECTED(max < min)) {
257170
zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
258171
RETURN_THROWS();
259172
}
260173

261-
RETURN_DOUBLE(getFloat_closed_closed(randomizer->algo, randomizer->status, min, max));
174+
RETURN_DOUBLE(php_random_gammasection_closed_closed(randomizer->algo, randomizer->status, min, max));
262175
} else if (zend_string_equals_literal(bounds_name, "OpenClosed")) {
263176
if (UNEXPECTED(max <= min)) {
264177
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
265178
RETURN_THROWS();
266179
}
267180

268-
RETURN_DOUBLE(getFloat_open_closed(randomizer->algo, randomizer->status, min, max));
181+
RETURN_DOUBLE(php_random_gammasection_open_closed(randomizer->algo, randomizer->status, min, max));
269182
} else if (zend_string_equals_literal(bounds_name, "OpenOpen")) {
270183
if (UNEXPECTED(max <= min)) {
271184
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
272185
RETURN_THROWS();
273186
}
274187

275-
RETURN_DOUBLE(getFloat_open_open(randomizer->algo, randomizer->status, min, max));
188+
RETURN_DOUBLE(php_random_gammasection_open_open(randomizer->algo, randomizer->status, min, max));
276189
} else {
277190
ZEND_UNREACHABLE();
278191
}

0 commit comments

Comments
 (0)