Skip to content

Commit 7b8d12a

Browse files
committed
random: Split the implementation of γ-section into its own file
1 parent e98017a commit 7b8d12a

File tree

5 files changed

+127
-94
lines changed

5 files changed

+127
-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: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Tim Düsterhus <timwolla@php.net> |
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+
26+
/* This file implements the γ-section algorithm as published in:
27+
*
28+
* Drawing Random Floating-Point Numbers from an Interval. Frédéric
29+
* Goualard, ACM Trans. Model. Comput. Simul., 32:3, 2022.
30+
* https://doi.org/10.1145/3503512
31+
*/
32+
33+
static double gamma_low(double x)
34+
{
35+
return x - nextdown(x);
36+
}
37+
38+
static double gamma_high(double x)
39+
{
40+
return nextup(x) - x;
41+
}
42+
43+
static double gamma_max(double x, double y)
44+
{
45+
return (fabs(x) > fabs(y)) ? gamma_high(x) : gamma_low(y);
46+
}
47+
48+
static uint64_t ceilint(double a, double b, double g)
49+
{
50+
double s = b / g - a / g;
51+
double e;
52+
53+
if (fabs(a) <= fabs(b)) {
54+
e = -a / g - (s - b / g);
55+
} else {
56+
e = b / g - (s + a / g);
57+
}
58+
59+
double si = ceil(s);
60+
61+
return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0);
62+
}
63+
64+
PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max)
65+
{
66+
double g = gamma_max(min, max);
67+
uint64_t hi = ceilint(min, max, g);
68+
uint64_t k = algo->range(status, 1, hi);
69+
70+
if (fabs(min) <= fabs(max)) {
71+
return k == hi ? min : max - k * g;
72+
} else {
73+
return min + (k - 1) * g;
74+
}
75+
}
76+
77+
PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
78+
{
79+
double g = gamma_max(min, max);
80+
uint64_t hi = ceilint(min, max, g);
81+
uint64_t k = algo->range(status, 0, hi);
82+
83+
if (fabs(min) <= fabs(max)) {
84+
return k == hi ? min : max - k * g;
85+
} else {
86+
return k == hi ? max : min + k * g;
87+
}
88+
}
89+
90+
PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
91+
{
92+
double g = gamma_max(min, max);
93+
uint64_t hi = ceilint(min, max, g);
94+
uint64_t k = algo->range(status, 0, hi - 1);
95+
96+
if (fabs(min) <= fabs(max)) {
97+
return max - k * g;
98+
} else {
99+
return k == (hi - 1) ? max : min + (k + 1) * g;
100+
}
101+
}
102+
103+
PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max)
104+
{
105+
double g = gamma_max(min, max);
106+
uint64_t hi = ceilint(min, max, g);
107+
uint64_t k = algo->range(status, 1, hi - 1);
108+
109+
if (fabs(min) <= fabs(max)) {
110+
return max - k * g;
111+
} else {
112+
return min + k * g;
113+
}
114+
}

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 - nextdown(x);
136-
}
137-
138-
static double getFloat_gamma_high(double x)
139-
{
140-
return nextup(x) - 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)