GRASS 8 Programmer's Manual 8.6.0dev(2026)-4bb960b182
Loading...
Searching...
No Matches
lrand48.c
Go to the documentation of this file.
1/*!
2 * \file lib/gis/lrand48.c
3 *
4 * \brief GIS Library - Pseudo-random number generation
5 *
6 * The generator is the standard drand48 linear congruential generator
7 * X' = (A * X + B) mod 2^48 with A = 0x5DEECE66D and B = 0xB.
8 *
9 * When C11 atomic operations are available, the generator state is
10 * advanced with an atomic compare-and-swap and the generating functions
11 * are thread-safe: the sequence of generated values for a given seed is
12 * the same as in a single-threaded run. Which thread receives which
13 * value depends on scheduling, so results are fully reproducible only
14 * with single-threaded execution. Without C11 atomics (notably MSVC,
15 * which defines __STDC_NO_ATOMICS__), the generator falls back to plain
16 * state updates, so multi-threaded usage is safe only when compiled
17 * with C11 atomics.
18 *
19 * The seeding functions are not thread-safe; see G_srand48().
20 *
21 * SPDX-FileCopyrightText: 2014-2026 GRASS Development Team
22 * SPDX-License-Identifier: GPL-2.0-or-later
23 *
24 * \authors Glynn Clements, Maris Nartiss (thread safety)
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <errno.h>
31
32#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \
33 !defined(__STDC_NO_ATOMICS__)
34#include <stdatomic.h>
35#include <stdint.h>
36#define LRAND48_ATOMIC 1
37#else
38#define LRAND48_ATOMIC 0
39#endif
40
41#include <grass/gis.h>
42#include <grass/glocale.h>
43
44#ifdef HAVE_GETTIMEOFDAY
45#include <sys/time.h>
46#else
47#include <time.h>
48#endif
49
50#include <sys/types.h>
51#include <unistd.h>
52
53typedef unsigned short uint16;
54typedef unsigned int uint32;
55typedef signed int int32;
56
57#if LRAND48_ATOMIC
58
59/* The whole 48-bit state is kept in one atomic integer so that it can be
60 * advanced in one compare-and-swap: a successful swap is exactly one
61 * generator step, giving the same sequence of states as a single-threaded
62 * run. The multiplication may wrap around at 2^64; that does not change
63 * the result modulo 2^48. */
65
66#define LCG_A UINT64_C(0x5DEECE66D)
67#define LCG_B UINT64_C(0xB)
68#define MASK48 UINT64_C(0xFFFFFFFFFFFF)
69
70#else
71
72static uint16 x0, x1, x2;
73static const uint32 a0 = 0xE66D;
74static const uint32 a1 = 0xDEEC;
75static const uint32 a2 = 0x5;
76
77static const uint32 b0 = 0xB;
78
79#endif /* LRAND48_ATOMIC */
80
81static int seeded;
82
83#define LO(x) ((x) & 0xFFFFU)
84#define HI(x) ((x) >> 16)
85
86/*!
87 * \brief Seed the pseudo-random number generator
88 *
89 * This function is not thread-safe. In a multi-threaded program, call
90 * `G_srand48()` once *before* starting the worker threads; it must not
91 * run concurrently with another thread seeding or generating values.
92 *
93 * \param[in] seedval 32-bit integer used to seed the PRNG
94 */
96{
97 uint32 x = (uint32) * (unsigned long *)&seedval;
98
99#if LRAND48_ATOMIC
100 atomic_store(&state, ((uint_least64_t)x << 16) | 0x330E);
101#else
102 x2 = (uint16)HI(x);
103 x1 = (uint16)LO(x);
104 x0 = (uint16)0x330E;
105#endif
106 seeded = 1;
107}
108
109/*!
110 * \brief Seed the pseudo-random number generator from the time and PID
111 *
112 * A weak hash of the current time and PID is generated and used to
113 * seed the PRNG
114 *
115 * This function is not thread-safe. In a multi-threaded program, call
116 * `G_srand48_auto()` once *before* starting the worker threads; it must
117 * not run concurrently with another thread seeding or generating values.
118 *
119 * \return generated seed value passed to G_srand48()
120 */
122{
123 unsigned long seed;
124 char *grass_random_seed = getenv("GRASS_RANDOM_SEED");
125
127 grass_random_seed = getenv("SOURCE_DATE_EPOCH");
128 if (grass_random_seed) {
130 }
131 else {
132 seed = (unsigned long)getpid();
133
134#ifdef HAVE_GETTIMEOFDAY
135 {
136 struct timeval tv;
137
138 if (gettimeofday(&tv, NULL) < 0)
139 G_fatal_error(_("gettimeofday failed: %s"), strerror(errno));
140 seed += (unsigned long)tv.tv_sec;
141 seed += (unsigned long)tv.tv_usec;
142 }
143#else
144 {
145 time_t t = time(NULL);
146
147 seed += (unsigned long)t;
148 }
149#endif
150 }
151
152 G_srand48((long)seed);
153 return (long)seed;
154}
155
156#if LRAND48_ATOMIC
157
158/* Advance the generator by one step and return the new state. Callers
159 * derive their result from the returned value, not from shared state, so
160 * concurrent calls each get a distinct step of the sequence. */
161static uint_least64_t G__next(void)
162{
164 uint_least64_t next;
165
166 if (!seeded)
167 G_fatal_error(_("Pseudo-random number generator not seeded"));
168
169 do {
170 next = (LCG_A * cur + LCG_B) & MASK48;
172 &state, &cur, next, memory_order_relaxed, memory_order_relaxed));
173
174 return next;
175}
176
177#else
178
179static void G__next(void)
180{
181 uint32 a0x0 = a0 * x0;
182 uint32 a0x1 = a0 * x1;
183 uint32 a0x2 = a0 * x2;
184 uint32 a1x0 = a1 * x0;
185 uint32 a1x1 = a1 * x1;
186 uint32 a2x0 = a2 * x0;
187
188 uint32 y0 = LO(a0x0) + b0;
189 uint32 y1 = LO(a0x1) + LO(a1x0) + HI(a0x0);
190 uint32 y2 = LO(a0x2) + LO(a1x1) + LO(a2x0) + HI(a0x1) + HI(a1x0);
191
192 if (!seeded)
193 G_fatal_error(_("Pseudo-random number generator not seeded"));
194
195 x0 = (uint16)LO(y0);
196 y1 += HI(y0);
197 x1 = (uint16)LO(y1);
198 y2 += HI(y1);
199 x2 = (uint16)LO(y2);
200}
201
202#endif /* LRAND48_ATOMIC */
203
204/*!
205 * \brief Generate an integer in the range [0, 2^31)
206 *
207 * This function is thread-safe only when compiled with C11 atomics
208 * (see the comment at the top of the file).
209 *
210 * \return the generated value
211 */
212long G_lrand48(void)
213{
214#if LRAND48_ATOMIC
215 return (long)(G__next() >> 17);
216#else
217 uint32 r;
218
219 G__next();
220 r = ((uint32)x2 << 15) | ((uint32)x1 >> 1);
221 return (long)r;
222#endif
223}
224
225/*!
226 * \brief Generate an integer in the range [-2^31, 2^31)
227 *
228 * This function is thread-safe only when compiled with C11 atomics
229 * (see the comment at the top of the file).
230 *
231 * \return the generated value
232 */
233long G_mrand48(void)
234{
235#if LRAND48_ATOMIC
236 uint32 r = (uint32)(G__next() >> 16);
237
238 return (long)(int32)r;
239#else
240 uint32 r;
241
242 G__next();
243 r = ((uint32)x2 << 16) | ((uint32)x1);
244 return (long)(int32)r;
245#endif
246}
247
248/*!
249 * \brief Generate a floating-point value in the range [0,1)
250 *
251 * This function is thread-safe only when compiled with C11 atomics
252 * (see the comment at the top of the file).
253 *
254 * \return the generated value
255 */
256double G_drand48(void)
257{
258#if LRAND48_ATOMIC
259 /* The state is below 2^53, so the conversion to double is exact. */
260 return (double)G__next() / 281474976710656.0; /* 2^48 */
261#else
262 double r = 0.0;
263
264 G__next();
265 r += x2;
266 r *= 0x10000;
267 r += x1;
268 r *= 0x10000;
269 r += x0;
270 r /= 281474976710656.0; /* 2^48 */
271 return r;
272#endif
273}
274
275/*
276
277 Test program
278
279 int main(int argc, char **argv)
280 {
281 long s = (argc > 1) ? atol(argv[1]) : 0;
282 int i;
283
284 srand48(s);
285 G_srand48(s);
286
287 for (i = 0; i < 100; i++) {
288 printf("%.50f %.50f\n", drand48(), G_drand48());
289 printf("%lu %lu\n", lrand48(), G_lrand48());
290 printf("%ld %ld\n", mrand48(), G_mrand48());
291 }
292
293 return 0;
294 }
295
296 */
#define NULL
Definition ccmath.h:32
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
#define _(str)
Definition glocale.h:10
unsigned short uint16
Definition lrand48.c:53
unsigned int uint32
Definition lrand48.c:54
long G_mrand48(void)
Generate an integer in the range [-2^31, 2^31)
Definition lrand48.c:233
long G_lrand48(void)
Generate an integer in the range [0, 2^31)
Definition lrand48.c:212
long G_srand48_auto(void)
Seed the pseudo-random number generator from the time and PID.
Definition lrand48.c:121
signed int int32
Definition lrand48.c:55
#define HI(x)
Definition lrand48.c:84
void G_srand48(long seedval)
Seed the pseudo-random number generator.
Definition lrand48.c:95
#define LO(x)
Definition lrand48.c:83
double G_drand48(void)
Generate a floating-point value in the range [0,1)
Definition lrand48.c:256
struct state state
Definition parser.c:101
double t
Definition r_raster.c:37
double r
Definition r_raster.c:37
int gettimeofday(struct timeval *, struct timezone *)
#define getpid
Definition unistd.h:20
#define x