Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to the new SDL_rand capability #10063

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 51 additions & 22 deletions include/SDL3/SDL_stdinc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,10 @@ extern SDL_DECLSPEC int SDLCALL SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRI
extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2);

/**
* Seed the pseudo-random number generator
* Seed the pseudo-random number generator.
*
* Reusing the seed number will cause SDL_rand() to repeat the same stream
* of 'random' numbers.
*
* \param seed the value to use as a random number seed, or 0 to use
* SDL_GetPerformanceCounter().
Expand All @@ -1275,50 +1278,76 @@ extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STR
extern SDL_DECLSPEC void SDLCALL SDL_srand(Uint64 seed);

/**
* Get a pseudo-random number.
* Get 32 pseudo-random bits.
*
* You likely want to use SDL_rand_n() to get a psuedo-randum number instead.
*
* If you want reproducible output, be sure to initialize with SDL_srand() first.
*
* There are no guarantees as to the quality of the random sequence produced,
* and this should not be used for cryptography or anything that requires good
* random distribution. There are many random number libraries available with
* different characteristics and you should pick one of those to meet any
* serious needs.
* and this should not be used for security (cryptography, passwords) or where
* money is on the line (loot-boxes, casinos). There are many random number
* libraries available with different characteristics and you should pick one of
* those to meet any serious needs.
*
* \returns a random value in the range of [0-SDL_MAX_UINT32].
*
* \threadsafety All calls should be made from a single thread, use
* SDL_rand_r() when using multiple threads.
* \threadsafety All calls should be made from a single thread
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_rand_r
* \sa SDL_srand
* \sa SDL_rand_n
* \sa SDL_rand_float
*/
extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand(void);

/**
* Get a pseudo-random number.
* Generates a pseudo-random number less than n
*
* The method used is faster and of better quality than `SDL_rand() % n`.
* However just like with `SDL_rand() % n`, bias increases with larger n.
* Odds are better than 99.9% even for n under 1 million.
*
* Example: to simulate a d6 use `SDL_rand_n(6) + 1`
* The +1 converts 0..5 to 1..6
*
* There are no guarantees as to the quality of the random sequence produced,
* and this should not be used for cryptography or anything that requires good
* random distribution. There are many random number libraries available with
* different characteristics and you should pick one of those to meet any
* serious needs.
* and this should not be used for security (cryptography, passwords) or where
* money is on the line (loot-boxes, casinos). There are many random number
* libraries available with different characteristics and you should pick one of
* those to meet any serious needs.
*
* \param n the number of possible values
*
* \param state a pointer to a 64-bit seed value that will be updated with
* each call to SDL_rand_r(). If the value of the seed is 0, it
* will be initialized with SDL_GetPerformanceCounter().
* \returns a random value in the range of [0-SDL_MAX_UINT32], or 0 if state
* is NULL.
* \returns a random value in the range of [0 .. n-1]
*
* \threadsafety This can be called from any thread, however each thread
* should pass its own state pointer.
* \threadsafety All calls should be made from a single thread
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_rand
*/
extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_r(Uint64 *state);
extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_n(Uint32 n);

/**
* Generates a uniform pseudo-random floating point number less than 1.0
*
* There are no guarantees as to the quality of the random sequence produced,
* and this should not be used for security (cryptography, passwords) or where
* money is on the line (loot-boxes, casinos). There are many random number
* libraries available with different characteristics and you should pick one of
* those to meet any serious needs.
*
* \returns a random value in the range of [0.0, 1.0)
*
* \threadsafety All calls should be made from a single thread
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_rand
*/
extern SDL_DECLSPEC float SDLCALL SDL_rand_float(void);

#ifndef SDL_PI_D
#define SDL_PI_D 3.141592653589793238462643383279502884 /**< pi (double) */
Expand Down
3 changes: 2 additions & 1 deletion src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,8 @@
#define SDL_qsort SDL_qsort_REAL
#define SDL_qsort_r SDL_qsort_r_REAL
#define SDL_rand SDL_rand_REAL
#define SDL_rand_r SDL_rand_r_REAL
#define SDL_rand_float SDL_rand_float_REAL
#define SDL_rand_n SDL_rand_n_REAL
#define SDL_realloc SDL_realloc_REAL
#define SDL_round SDL_round_REAL
#define SDL_roundf SDL_roundf_REAL
Expand Down
3 changes: 2 additions & 1 deletion src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,8 @@ SDL_DYNAPI_PROC(float,SDL_powf,(float a, float b),(a,b),return)
SDL_DYNAPI_PROC(void,SDL_qsort,(void *a, size_t b, size_t c, SDL_CompareCallback d),(a,b,c,d),)
SDL_DYNAPI_PROC(void,SDL_qsort_r,(void *a, size_t b, size_t c, SDL_CompareCallback_r d, void *e),(a,b,c,d,e),)
SDL_DYNAPI_PROC(Uint32,SDL_rand,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_rand_r,(Uint64 *a),(a),return)
SDL_DYNAPI_PROC(float,SDL_rand_float,(void),(),return)
SDL_DYNAPI_PROC(Uint32,SDL_rand_n,(Uint32 a),(a),return)
SDL_DYNAPI_PROC(void*,SDL_realloc,(void *a, size_t b),(a,b),return)
SDL_DYNAPI_PROC(double,SDL_round,(double a),(a),return)
SDL_DYNAPI_PROC(float,SDL_roundf,(float a),(a),return)
Expand Down
66 changes: 30 additions & 36 deletions src/stdlib/SDL_random.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,58 +23,52 @@
/* This file contains portable random functions for SDL */

static Uint64 SDL_rand_state;
static SDL_bool SDL_rand_initialized = SDL_FALSE;

void SDL_srand(Uint64 seed)
{
if (!seed) {
seed = SDL_GetPerformanceCounter();
}
SDL_rand_state = seed;
SDL_rand_initialized = SDL_TRUE;
}

Uint32 SDL_rand(void)
{
return SDL_rand_r(&SDL_rand_state);
if(!SDL_rand_initialized) {
SDL_srand(0);
}

// The C and A parameters of this LCG have been chosen based on hundreds
// of core-hours of testing with PractRand and TestU01's Crush.
// Using a 32-bit A improves performance on 32-bit architectures.
// C can be any odd number, but < 256 generates smaller code on ARM32
// These values perform as well as a full 64-bit implementation against
// Crush and PractRand. Plus, their worst-case performance is better
// than common 64-bit constants when tested against PractRand using seeds
// with only a single bit set.

// We tested all 32-bit and 33-bit A with all C < 256 from a v2 of:
// Steele GL, Vigna S. Computationally easy, spectrally good multipliers
// for congruential pseudorandom number generators.
// Softw Pract Exper. 2022;52(2):443-458. doi: 10.1002/spe.3030
// https://arxiv.org/abs/2001.05304v2

SDL_rand_state = SDL_rand_state * 0xff1cd035ul + 0x05;

// Only return top 32 bits because they have a longer period
return (Uint32)(SDL_rand_state >> 32);
}

/*
* Return a number between [0, n)
* Fast but slightly biased. Don't run your casino with this.
*/
Sint32 SDL_rand_n(Sint32 n)
Uint32 SDL_rand_n(Uint32 n)
{
// On 32-bit arch, the compiler will optimize to a single 32-bit multiply
Uint64 val = (Uint64)SDL_rand() * n;
return (Sint32)(val >> 32);
return (Uint32)(val >> 32);
}

/*
* Random float in range [0,1)
*/
float SDL_rand_float(void)
{
return (SDL_rand() >> (32-24)) * 0x1p-24f;
}

/* A fast psuedo-random number generator.
* Not suitable for cryptography or gambling
*/
Uint32 SDL_rand_r(Uint64 *state)
{
if (!state) {
return 0;
}

if (!*state) {
*state = SDL_GetPerformanceCounter();
}

// Multiplier from Table 6 of
// Steele GL, Vigna S. Computationally easy, spectrally good multipliers
// for congruential pseudorandom number generators.
// Softw Pract Exper. 2022;52(2):443-458. doi: 10.1002/spe.3030

// 32-bit 'a' improves performance on 32-bit architectures
// 'c' can be any odd number, but < 256 generates smaller code on some arch
*state = *state * 0xf9b25d65ul + 0xFD;

// Only return top 32 bits because they have a longer period
return (Uint32)(*state >> 32);
}
Loading