1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-12 16:21:30 +03:00
postgres/src/common/pg_prng.c
Dean Rasheed e6341323a8 Add functions to generate random numbers in a specified range.
This adds 3 new variants of the random() function:

    random(min integer, max integer) returns integer
    random(min bigint, max bigint) returns bigint
    random(min numeric, max numeric) returns numeric

Each returns a random number x in the range min <= x <= max.

For the numeric function, the number of digits after the decimal point
is equal to the number of digits that "min" or "max" has after the
decimal point, whichever has more.

The main entry points for these functions are in a new C source file.
The existing random(), random_normal(), and setseed() functions are
moved there too, so that they can all share the same PRNG state, which
is kept private to that file.

Dean Rasheed, reviewed by Jian He, David Zhang, Aleksander Alekseev,
and Tomas Vondra.

Discussion: https://postgr.es/m/CAEZATCV89Vxuq93xQdmc0t-0Y2zeeNQTdsjbmV7dyFBPykbV4Q@mail.gmail.com
2024-03-27 10:12:39 +00:00

319 lines
7.2 KiB
C

/*-------------------------------------------------------------------------
*
* Pseudo-Random Number Generator
*
* We use Blackman and Vigna's xoroshiro128** 1.0 algorithm
* to have a small, fast PRNG suitable for generating reasonably
* good-quality 64-bit data. This should not be considered
* cryptographically strong, however.
*
* About these generators: https://prng.di.unimi.it/
* See also https://en.wikipedia.org/wiki/List_of_random_number_generators
*
* Copyright (c) 2021-2024, PostgreSQL Global Development Group
*
* src/common/pg_prng.c
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include <math.h>
#include "common/pg_prng.h"
#include "port/pg_bitutils.h"
/* X/Open (XSI) requires <math.h> to provide M_PI, but core POSIX does not */
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* process-wide state vector */
pg_prng_state pg_global_prng_state;
/*
* 64-bit rotate left
*/
static inline uint64
rotl(uint64 x, int bits)
{
return (x << bits) | (x >> (64 - bits));
}
/*
* The basic xoroshiro128** algorithm.
* Generates and returns a 64-bit uniformly distributed number,
* updating the state vector for next time.
*
* Note: the state vector must not be all-zeroes, as that is a fixed point.
*/
static uint64
xoroshiro128ss(pg_prng_state *state)
{
uint64 s0 = state->s0,
sx = state->s1 ^ s0,
val = rotl(s0 * 5, 7) * 9;
/* update state */
state->s0 = rotl(s0, 24) ^ sx ^ (sx << 16);
state->s1 = rotl(sx, 37);
return val;
}
/*
* We use this generator just to fill the xoroshiro128** state vector
* from a 64-bit seed.
*/
static uint64
splitmix64(uint64 *state)
{
/* state update */
uint64 val = (*state += UINT64CONST(0x9E3779B97f4A7C15));
/* value extraction */
val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9);
val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB);
return val ^ (val >> 31);
}
/*
* Initialize the PRNG state from a 64-bit integer,
* taking care that we don't produce all-zeroes.
*/
void
pg_prng_seed(pg_prng_state *state, uint64 seed)
{
state->s0 = splitmix64(&seed);
state->s1 = splitmix64(&seed);
/* Let's just make sure we didn't get all-zeroes */
(void) pg_prng_seed_check(state);
}
/*
* Initialize the PRNG state from a double in the range [-1.0, 1.0],
* taking care that we don't produce all-zeroes.
*/
void
pg_prng_fseed(pg_prng_state *state, double fseed)
{
/* Assume there's about 52 mantissa bits; the sign contributes too. */
int64 seed = ((double) ((UINT64CONST(1) << 52) - 1)) * fseed;
pg_prng_seed(state, (uint64) seed);
}
/*
* Validate a PRNG seed value.
*/
bool
pg_prng_seed_check(pg_prng_state *state)
{
/*
* If the seeding mechanism chanced to produce all-zeroes, insert
* something nonzero. Anything would do; use Knuth's LCG parameters.
*/
if (unlikely(state->s0 == 0 && state->s1 == 0))
{
state->s0 = UINT64CONST(0x5851F42D4C957F2D);
state->s1 = UINT64CONST(0x14057B7EF767814F);
}
/* As a convenience for the pg_prng_strong_seed macro, return true */
return true;
}
/*
* Select a random uint64 uniformly from the range [0, PG_UINT64_MAX].
*/
uint64
pg_prng_uint64(pg_prng_state *state)
{
return xoroshiro128ss(state);
}
/*
* Select a random uint64 uniformly from the range [rmin, rmax].
* If the range is empty, rmin is always produced.
*/
uint64
pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax)
{
uint64 val;
if (likely(rmax > rmin))
{
/*
* Use bitmask rejection method to generate an offset in 0..range.
* Each generated val is less than twice "range", so on average we
* should not have to iterate more than twice.
*/
uint64 range = rmax - rmin;
uint32 rshift = 63 - pg_leftmost_one_pos64(range);
do
{
val = xoroshiro128ss(state) >> rshift;
} while (val > range);
}
else
val = 0;
return rmin + val;
}
/*
* Select a random int64 uniformly from the range [PG_INT64_MIN, PG_INT64_MAX].
*/
int64
pg_prng_int64(pg_prng_state *state)
{
return (int64) xoroshiro128ss(state);
}
/*
* Select a random int64 uniformly from the range [0, PG_INT64_MAX].
*/
int64
pg_prng_int64p(pg_prng_state *state)
{
return (int64) (xoroshiro128ss(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF));
}
/*
* Select a random int64 uniformly from the range [rmin, rmax].
* If the range is empty, rmin is always produced.
*/
int64
pg_prng_int64_range(pg_prng_state *state, int64 rmin, int64 rmax)
{
int64 val;
if (likely(rmax > rmin))
{
uint64 uval;
/*
* Use pg_prng_uint64_range(). Can't simply pass it rmin and rmax,
* since (uint64) rmin will be larger than (uint64) rmax if rmin < 0.
*/
uval = (uint64) rmin +
pg_prng_uint64_range(state, 0, (uint64) rmax - (uint64) rmin);
/*
* Safely convert back to int64, avoiding implementation-defined
* behavior for values larger than PG_INT64_MAX. Modern compilers
* will reduce this to a simple assignment.
*/
if (uval > PG_INT64_MAX)
val = (int64) (uval - PG_INT64_MIN) + PG_INT64_MIN;
else
val = (int64) uval;
}
else
val = rmin;
return val;
}
/*
* Select a random uint32 uniformly from the range [0, PG_UINT32_MAX].
*/
uint32
pg_prng_uint32(pg_prng_state *state)
{
/*
* Although xoroshiro128** is not known to have any weaknesses in
* randomness of low-order bits, we prefer to use the upper bits of its
* result here and below.
*/
uint64 v = xoroshiro128ss(state);
return (uint32) (v >> 32);
}
/*
* Select a random int32 uniformly from the range [PG_INT32_MIN, PG_INT32_MAX].
*/
int32
pg_prng_int32(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
return (int32) (v >> 32);
}
/*
* Select a random int32 uniformly from the range [0, PG_INT32_MAX].
*/
int32
pg_prng_int32p(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
return (int32) (v >> 33);
}
/*
* Select a random double uniformly from the range [0.0, 1.0).
*
* Note: if you want a result in the range (0.0, 1.0], the standard way
* to get that is "1.0 - pg_prng_double(state)".
*/
double
pg_prng_double(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
/*
* As above, assume there's 52 mantissa bits in a double. This result
* could round to 1.0 if double's precision is less than that; but we
* assume IEEE float arithmetic elsewhere in Postgres, so this seems OK.
*/
return ldexp((double) (v >> (64 - 52)), -52);
}
/*
* Select a random double from the normal distribution with
* mean = 0.0 and stddev = 1.0.
*
* To get a result from a different normal distribution use
* STDDEV * pg_prng_double_normal() + MEAN
*
* Uses https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
*/
double
pg_prng_double_normal(pg_prng_state *state)
{
double u1,
u2,
z0;
/*
* pg_prng_double generates [0, 1), but for the basic version of the
* Box-Muller transform the two uniformly distributed random numbers are
* expected to be in (0, 1]; in particular we'd better not compute log(0).
*/
u1 = 1.0 - pg_prng_double(state);
u2 = 1.0 - pg_prng_double(state);
/* Apply Box-Muller transform to get one normal-valued output */
z0 = sqrt(-2.0 * log(u1)) * sin(2.0 * M_PI * u2);
return z0;
}
/*
* Select a random boolean value.
*/
bool
pg_prng_bool(pg_prng_state *state)
{
uint64 v = xoroshiro128ss(state);
return (bool) (v >> 63);
}