diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index 4544980d0..f4d9c9fa8 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/include/SDL3/SDL_stdinc.h @@ -1273,46 +1273,19 @@ extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STR * * \since This function is available since SDL 3.0.0. * - * \sa SDL_rand_n - * \sa SDL_rand_float - * \sa SDL_rand_bits + * \sa SDL_rand + * \sa SDL_randf */ extern SDL_DECLSPEC void SDLCALL SDL_srand(Uint64 seed); /** - * Generates 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 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 - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_srand - * \sa SDL_rand_n - * \sa SDL_rand_float - */ -extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits(void); - -/** - * Generates a pseudo-random number less than n for positive n + * Generate a pseudo-random number less than n for positive n * * The method used is faster and of better quality than `rand() % n`. Odds are * roughly 99.9% even for n = 1 million. Evenness is better for smaller n, and * much worse as n gets bigger. * - * Example: to simulate a d6 use `SDL_rand_n(6) + 1` The +1 converts 0..5 to + * Example: to simulate a d6 use `SDL_rand(6) + 1` The +1 converts 0..5 to * 1..6 * * If you want reproducible output, be sure to initialize with SDL_srand() @@ -1332,12 +1305,12 @@ extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits(void); * \since This function is available since SDL 3.0.0. * * \sa SDL_srand - * \sa SDL_rand_float + * \sa SDL_randf */ -extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_n(Sint32 n); +extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand(Sint32 n); /** - * Generates a uniform pseudo-random floating point number less than 1.0 + * Generate a uniform pseudo-random floating point number less than 1.0 * * If you want reproducible output, be sure to initialize with SDL_srand() * first. @@ -1355,9 +1328,88 @@ extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_n(Sint32 n); * \since This function is available since SDL 3.0.0. * * \sa SDL_srand - * \sa SDL_rand_n + * \sa SDL_rand */ -extern SDL_DECLSPEC float SDLCALL SDL_rand_float(void); +extern SDL_DECLSPEC float SDLCALL SDL_randf(void); + +/** + * Generate a pseudo-random number less than n for positive n + * + * The method used is faster and of better quality than `rand() % n`. Odds are + * roughly 99.9% even for n = 1 million. Evenness is better for smaller n, and + * much worse as n gets bigger. + * + * Example: to simulate a d6 use `SDL_rand_r(state, 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 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 state a pointer to the current random number state, this may not be NULL. + * \param n the number of possible outcomes. n must be positive. + * \returns a random value in the range of [0 .. n-1]. + * + * \threadsafety This function is thread-safe, as long as the state pointer isn't shared between threads. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_rand + * \sa SDL_rand_bits_r + * \sa SDL_randf_r + */ +extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_r(Uint64 *state, Sint32 n); + +/** + * Generate a uniform pseudo-random floating point number less than 1.0 + * + * 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 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 state a pointer to the current random number state, this may not be NULL. + * \returns a random value in the range of [0.0, 1.0). + * + * \threadsafety This function is thread-safe, as long as the state pointer isn't shared between threads. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_rand_bits_r + * \sa SDL_rand_r + * \sa SDL_randf + */ +extern SDL_DECLSPEC float SDLCALL SDL_randf_r(Uint64 *state); + +/** + * Generate 32 pseudo-random bits. + * + * You likely want to use SDL_rand_r() to get a psuedo-randum number instead. + * + * 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. + * + * \param state a pointer to the current random number state, this may not be NULL. + * \returns a random value in the range of [0-SDL_MAX_UINT32]. + * + * \threadsafety This function is thread-safe, as long as the state pointer isn't shared between threads. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_rand_r + * \sa SDL_randf_r + */ +extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits_r(Uint64 *state); + #ifndef SDL_PI_D #define SDL_PI_D 3.141592653589793238462643383279502884 /**< pi (double) */ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index cf0d4ac6a..261f6c4e5 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -961,9 +961,11 @@ SDL3_0.0.0 { SDL_powf; SDL_qsort; SDL_qsort_r; - SDL_rand_bits; - SDL_rand_float; - SDL_rand_n; + SDL_rand; + SDL_rand_bits_r; + SDL_rand_r; + SDL_randf; + SDL_randf_r; SDL_realloc; SDL_round; SDL_roundf; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 3d0eaf0b7..e3a1e4c90 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -986,9 +986,11 @@ #define SDL_powf SDL_powf_REAL #define SDL_qsort SDL_qsort_REAL #define SDL_qsort_r SDL_qsort_r_REAL -#define SDL_rand_bits SDL_rand_bits_REAL -#define SDL_rand_float SDL_rand_float_REAL -#define SDL_rand_n SDL_rand_n_REAL +#define SDL_rand SDL_rand_REAL +#define SDL_rand_bits_r SDL_rand_bits_r_REAL +#define SDL_rand_r SDL_rand_r_REAL +#define SDL_randf SDL_randf_REAL +#define SDL_randf_r SDL_randf_r_REAL #define SDL_realloc SDL_realloc_REAL #define SDL_round SDL_round_REAL #define SDL_roundf SDL_roundf_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d2217995f..f34673000 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -995,9 +995,11 @@ SDL_DYNAPI_PROC(double,SDL_pow,(double a, double b),(a,b),return) 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_bits,(void),(),return) -SDL_DYNAPI_PROC(float,SDL_rand_float,(void),(),return) -SDL_DYNAPI_PROC(Sint32,SDL_rand_n,(Sint32 a),(a),return) +SDL_DYNAPI_PROC(Sint32,SDL_rand,(Sint32 a),(a),return) +SDL_DYNAPI_PROC(Uint32,SDL_rand_bits_r,(Uint64 *a),(a),return) +SDL_DYNAPI_PROC(Sint32,SDL_rand_r,(Uint64 *a, Sint32 b),(a,b),return) +SDL_DYNAPI_PROC(float,SDL_randf,(void),(),return) +SDL_DYNAPI_PROC(float,SDL_randf_r,(Uint64 *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) diff --git a/src/stdlib/SDL_random.c b/src/stdlib/SDL_random.c index 200743173..1ad0b3ea5 100644 --- a/src/stdlib/SDL_random.c +++ b/src/stdlib/SDL_random.c @@ -34,12 +34,30 @@ void SDL_srand(Uint64 seed) SDL_rand_initialized = SDL_TRUE; } -Uint32 SDL_rand_bits(void) +Sint32 SDL_rand(Sint32 n) { if (!SDL_rand_initialized) { SDL_srand(0); } + return SDL_rand_r(&SDL_rand_state, n); +} + +float SDL_randf(void) +{ + if (!SDL_rand_initialized) { + SDL_srand(0); + } + + return SDL_randf_r(&SDL_rand_state); +} + +Uint32 SDL_rand_bits_r(Uint64 *state) +{ + if (!state) { + return 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. @@ -55,13 +73,13 @@ Uint32 SDL_rand_bits(void) // 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; + *state = *state * 0xff1cd035ul + 0x05; // Only return top 32 bits because they have a longer period - return (Uint32)(SDL_rand_state >> 32); + return (Uint32)(*state >> 32); } -Sint32 SDL_rand_n(Sint32 n) +Sint32 SDL_rand_r(Uint64 *state, Sint32 n) { // Algorithm: get 32 bits from SDL_rand_bits() and treat it as a 0.32 bit // fixed point number. Multiply by the 31.0 bit n to get a 31.32 bit @@ -70,18 +88,19 @@ Sint32 SDL_rand_n(Sint32 n) if (n < 0) { // The algorithm looks like it works for numbers < 0 but it has an // infintesimal chance of returning a value out of range. - // Returning -SDL_rand_n(abs(n)) blows up at INT_MIN instead. + // Returning -SDL_rand(abs(n)) blows up at INT_MIN instead. // It's easier to just say no. return 0; } // On 32-bit arch, the compiler will optimize to a single 32-bit multiply - Uint64 val = (Uint64)SDL_rand_bits() * n; + Uint64 val = (Uint64)SDL_rand_bits_r(state) * n; return (Sint32)(val >> 32); } -float SDL_rand_float(void) +float SDL_randf_r(Uint64 *state) { // Note: its using 24 bits because float has 23 bits significand + 1 implicit bit - return (SDL_rand_bits() >> (32 - 24)) * 0x1p-24f; + return (SDL_rand_bits_r(state) >> (32 - 24)) * 0x1p-24f; } + diff --git a/test/checkkeysthreads.c b/test/checkkeysthreads.c index 881de8f94..e236b8592 100644 --- a/test/checkkeysthreads.c +++ b/test/checkkeysthreads.c @@ -231,7 +231,7 @@ static int SDLCALL ping_thread(void *ptr) sdlevent.type = SDL_EVENT_KEY_DOWN; sdlevent.key.key = SDLK_1; SDL_PushEvent(&sdlevent); - SDL_Delay(1000 + SDL_rand_n(1000)); + SDL_Delay(1000 + SDL_rand(1000)); } return cnt; } diff --git a/test/testdraw.c b/test/testdraw.c index 5622c0e76..242b49ec8 100644 --- a/test/testdraw.c +++ b/test/testdraw.c @@ -72,8 +72,8 @@ static void DrawPoints(SDL_Renderer *renderer) SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color, (Uint8)current_color, (Uint8)current_alpha); - x = (float)SDL_rand_n(viewport.w); - y = (float)SDL_rand_n(viewport.h); + x = (float)SDL_rand(viewport.w); + y = (float)SDL_rand(viewport.h); SDL_RenderPoint(renderer, x, y); } } @@ -120,10 +120,10 @@ static void DrawLines(SDL_Renderer *renderer) SDL_RenderLine(renderer, 0.0f, (float)(viewport.h / 2), (float)(viewport.w - 1), (float)(viewport.h / 2)); SDL_RenderLine(renderer, (float)(viewport.w / 2), 0.0f, (float)(viewport.w / 2), (float)(viewport.h - 1)); } else { - x1 = (float)(SDL_rand_n(viewport.w * 2) - viewport.w); - x2 = (float)(SDL_rand_n(viewport.w * 2) - viewport.w); - y1 = (float)(SDL_rand_n(viewport.h * 2) - viewport.h); - y2 = (float)(SDL_rand_n(viewport.h * 2) - viewport.h); + x1 = (float)(SDL_rand(viewport.w * 2) - viewport.w); + x2 = (float)(SDL_rand(viewport.w * 2) - viewport.w); + y1 = (float)(SDL_rand(viewport.h * 2) - viewport.h); + y2 = (float)(SDL_rand(viewport.h * 2) - viewport.h); SDL_RenderLine(renderer, x1, y1, x2, y2); } } @@ -165,10 +165,10 @@ static void DrawRects(SDL_Renderer *renderer) SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color, (Uint8)current_color, (Uint8)current_alpha); - rect.w = (float)SDL_rand_n(viewport.h / 2); - rect.h = (float)SDL_rand_n(viewport.h / 2); - rect.x = (float)((SDL_rand_n(viewport.w * 2) - viewport.w) - (rect.w / 2)); - rect.y = (float)((SDL_rand_n(viewport.h * 2) - viewport.h) - (rect.h / 2)); + rect.w = (float)SDL_rand(viewport.h / 2); + rect.h = (float)SDL_rand(viewport.h / 2); + rect.x = (float)((SDL_rand(viewport.w * 2) - viewport.w) - (rect.w / 2)); + rect.y = (float)((SDL_rand(viewport.h * 2) - viewport.h) - (rect.h / 2)); SDL_RenderFillRect(renderer, &rect); } } diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 1f717d1b7..3826be652 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -1485,15 +1485,15 @@ int main(int argc, char *argv[]) SDL_Rect viewport; SDL_GetRenderViewport(renderer, &viewport); for (i = 0; i < num_sprites; ++i) { - positions[i].x = (float)SDL_rand_n(viewport.w - sprite_w); - positions[i].y = (float)SDL_rand_n(viewport.h - sprite_h); + positions[i].x = (float)SDL_rand(viewport.w - sprite_w); + positions[i].y = (float)SDL_rand(viewport.h - sprite_h); positions[i].w = (float)sprite_w; positions[i].h = (float)sprite_h; velocities[i].x = 0.0f; velocities[i].y = 0.0f; while (velocities[i].x == 0.f || velocities[i].y == 0.f) { - velocities[i].x = (float)(SDL_rand_n(2 + 1) - 1); - velocities[i].y = (float)(SDL_rand_n(2 + 1) - 1); + velocities[i].x = (float)(SDL_rand(2 + 1) - 1); + velocities[i].y = (float)(SDL_rand(2 + 1) - 1); } } diff --git a/test/testintersections.c b/test/testintersections.c index 28c1d0d7a..ad16951cc 100644 --- a/test/testintersections.c +++ b/test/testintersections.c @@ -74,8 +74,8 @@ static void DrawPoints(SDL_Renderer *renderer) SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color, (Uint8)current_color, (Uint8)current_alpha); - x = (float)SDL_rand_n(viewport.w); - y = (float)SDL_rand_n(viewport.h); + x = (float)SDL_rand(viewport.w); + y = (float)SDL_rand(viewport.h); SDL_RenderPoint(renderer, x, y); } } @@ -231,20 +231,20 @@ static void loop(void *arg) break; case SDLK_l: add_line( - (float)SDL_rand_n(640), - (float)SDL_rand_n(480), - (float)SDL_rand_n(640), - (float)SDL_rand_n(480)); + (float)SDL_rand(640), + (float)SDL_rand(480), + (float)SDL_rand(640), + (float)SDL_rand(480)); break; case SDLK_R: num_rects = 0; break; case SDLK_r: add_rect( - (float)SDL_rand_n(640), - (float)SDL_rand_n(480), - (float)SDL_rand_n(640), - (float)SDL_rand_n(480)); + (float)SDL_rand(640), + (float)SDL_rand(480), + (float)SDL_rand(640), + (float)SDL_rand(480)); break; default: break; diff --git a/test/testnative.c b/test/testnative.c index 1df5ebd25..6a0db0757 100644 --- a/test/testnative.c +++ b/test/testnative.c @@ -189,15 +189,15 @@ int main(int argc, char *argv[]) quit(2); } for (i = 0; i < NUM_SPRITES; ++i) { - positions[i].x = (float)(SDL_rand_n(window_w - (int)sprite_w)); - positions[i].y = (float)(SDL_rand_n(window_h - (int)sprite_h)); + positions[i].x = (float)(SDL_rand(window_w - (int)sprite_w)); + positions[i].y = (float)(SDL_rand(window_h - (int)sprite_h)); positions[i].w = sprite_w; positions[i].h = sprite_h; velocities[i].x = 0.0f; velocities[i].y = 0.0f; while (velocities[i].x == 0.f && velocities[i].y == 0.f) { - velocities[i].x = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED); - velocities[i].y = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED); + velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED); + velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED); } } diff --git a/test/testspriteminimal.c b/test/testspriteminimal.c index a3e2e1c57..41704992d 100644 --- a/test/testspriteminimal.c +++ b/test/testspriteminimal.c @@ -135,15 +135,15 @@ int main(int argc, char *argv[]) /* Initialize the sprite positions */ for (i = 0; i < NUM_SPRITES; ++i) { - positions[i].x = (float)SDL_rand_n(WINDOW_WIDTH - sprite_w); - positions[i].y = (float)SDL_rand_n(WINDOW_HEIGHT - sprite_h); + positions[i].x = (float)SDL_rand(WINDOW_WIDTH - sprite_w); + positions[i].y = (float)SDL_rand(WINDOW_HEIGHT - sprite_h); positions[i].w = (float)sprite_w; positions[i].h = (float)sprite_h; velocities[i].x = 0.0f; velocities[i].y = 0.0f; while (velocities[i].x == 0.f && velocities[i].y == 0.f) { - velocities[i].x = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED); - velocities[i].y = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED); + velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED); + velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED); } } diff --git a/test/testwaylandcustom.c b/test/testwaylandcustom.c index 2703b5fae..38b61f8ff 100644 --- a/test/testwaylandcustom.c +++ b/test/testwaylandcustom.c @@ -97,15 +97,15 @@ static int InitSprites(void) } for (int i = 0; i < NUM_SPRITES; ++i) { - positions[i].x = (float)SDL_rand_n(WINDOW_WIDTH - sprite_w); - positions[i].y = (float)SDL_rand_n(WINDOW_HEIGHT - sprite_h); + positions[i].x = (float)SDL_rand(WINDOW_WIDTH - sprite_w); + positions[i].y = (float)SDL_rand(WINDOW_HEIGHT - sprite_h); positions[i].w = (float)sprite_w; positions[i].h = (float)sprite_h; velocities[i].x = 0.0f; velocities[i].y = 0.0f; while (velocities[i].x == 0.f && velocities[i].y == 0.f) { - velocities[i].x = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED); - velocities[i].y = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED); + velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED); + velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED); } }