Merge pull request #7532 from tagcup/pcg_prng
Replace the existing PRNG (Xorshift31) with (minimal) PCG-32.
This commit is contained in:
commit
bf05dab74f
7 changed files with 54 additions and 25 deletions
|
@ -31,8 +31,9 @@
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
|
||||||
#include "float.h"
|
#include "float.h"
|
||||||
uint32_t Math::default_seed=1;
|
#include "pcg.h"
|
||||||
|
|
||||||
|
pcg32_random_t Math::default_pcg = {1, PCG_DEFAULT_INC_64};
|
||||||
|
|
||||||
#define PHI 0x9e3779b9
|
#define PHI 0x9e3779b9
|
||||||
|
|
||||||
|
@ -40,28 +41,26 @@ uint32_t Math::default_seed=1;
|
||||||
static uint32_t Q[4096];
|
static uint32_t Q[4096];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t Math::rand_from_seed(uint32_t *seed) {
|
// TODO: we should eventually expose pcg.inc too
|
||||||
// Xorshift31 PRNG
|
uint32_t Math::rand_from_seed(uint64_t *seed) {
|
||||||
if ( *seed == 0 ) *seed = Math::RANDOM_MAX;
|
pcg32_random_t pcg = {*seed, PCG_DEFAULT_INC_64};
|
||||||
(*seed) ^= (*seed) << 13;
|
uint32_t r = pcg32_random_r(&pcg);
|
||||||
(*seed) ^= (*seed) >> 17;
|
*seed = pcg.state;
|
||||||
(*seed) ^= (*seed) << 5;
|
return r;
|
||||||
return (*seed) & Math::RANDOM_MAX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Math::seed(uint32_t x) {
|
void Math::seed(uint64_t x) {
|
||||||
default_seed=x;
|
default_pcg.state=x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Math::randomize() {
|
void Math::randomize() {
|
||||||
|
|
||||||
OS::Time time = OS::get_singleton()->get_time();
|
OS::Time time = OS::get_singleton()->get_time();
|
||||||
seed(OS::get_singleton()->get_ticks_usec()*(time.hour+1)*(time.min+1)*(time.sec+1)*rand()); /* *OS::get_singleton()->get_time().sec); // windows doesn't have get_time(), returns always 0 */
|
seed(OS::get_singleton()->get_ticks_usec()*(time.hour+1)*(time.min+1)*(time.sec+1)*rand()); // TODO: can be simplified.
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Math::rand() {
|
uint32_t Math::rand() {
|
||||||
|
return pcg32_random_r(&default_pcg);
|
||||||
return rand_from_seed(&default_seed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double Math::randf() {
|
double Math::randf() {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "typedefs.h"
|
#include "typedefs.h"
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
|
#include "pcg.h"
|
||||||
|
|
||||||
#ifndef NO_MATH_H
|
#ifndef NO_MATH_H
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -41,8 +42,8 @@
|
||||||
|
|
||||||
class Math {
|
class Math {
|
||||||
|
|
||||||
|
static pcg32_random_t default_pcg;
|
||||||
|
|
||||||
static uint32_t default_seed;
|
|
||||||
public:
|
public:
|
||||||
Math() {} // useless to instance
|
Math() {} // useless to instance
|
||||||
|
|
||||||
|
@ -150,12 +151,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint32_t rand_from_seed(uint32_t *seed);
|
static uint32_t rand_from_seed(uint64_t *seed);
|
||||||
|
|
||||||
static double ease(double p_x, double p_c);
|
static double ease(double p_x, double p_c);
|
||||||
static int step_decimals(double p_step);
|
static int step_decimals(double p_step);
|
||||||
static double stepify(double p_value,double p_step);
|
static double stepify(double p_value,double p_step);
|
||||||
static void seed(uint32_t x=0);
|
static void seed(uint64_t x=0);
|
||||||
static void randomize();
|
static void randomize();
|
||||||
static uint32_t larger_prime(uint32_t p_val);
|
static uint32_t larger_prime(uint32_t p_val);
|
||||||
static double dectime(double p_value,double p_amount, double p_step);
|
static double dectime(double p_value,double p_amount, double p_step);
|
||||||
|
|
15
core/math/pcg.cpp
Normal file
15
core/math/pcg.cpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
|
||||||
|
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
|
||||||
|
|
||||||
|
#include "pcg.h"
|
||||||
|
|
||||||
|
uint32_t pcg32_random_r(pcg32_random_t* rng)
|
||||||
|
{
|
||||||
|
uint64_t oldstate = rng->state;
|
||||||
|
// Advance internal state
|
||||||
|
rng->state = oldstate * 6364136223846793005ULL + (rng->inc|1);
|
||||||
|
// Calculate output function (XSH RR), uses old state for max ILP
|
||||||
|
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
|
||||||
|
uint32_t rot = oldstate >> 59u;
|
||||||
|
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
|
||||||
|
}
|
14
core/math/pcg.h
Normal file
14
core/math/pcg.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
|
||||||
|
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
|
||||||
|
|
||||||
|
#ifndef RANDOM_H
|
||||||
|
#define RANDOM_H
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
|
||||||
|
#define PCG_DEFAULT_INC_64 1442695040888963407ULL
|
||||||
|
|
||||||
|
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
|
||||||
|
uint32_t pcg32_random_r(pcg32_random_t* rng);
|
||||||
|
|
||||||
|
#endif // RANDOM_H
|
|
@ -463,7 +463,7 @@
|
||||||
<argument index="1" name="to" type="float">
|
<argument index="1" name="to" type="float">
|
||||||
</argument>
|
</argument>
|
||||||
<description>
|
<description>
|
||||||
Random range, any floating point value between 'from' and 'to'
|
Random range, any floating point value between 'from' and 'to'.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="rand_seed">
|
<method name="rand_seed">
|
||||||
|
@ -472,28 +472,28 @@
|
||||||
<argument index="0" name="seed" type="int">
|
<argument index="0" name="seed" type="int">
|
||||||
</argument>
|
</argument>
|
||||||
<description>
|
<description>
|
||||||
Random from seed, pass a seed and an array with both number and new seed is returned.
|
Random from seed: pass a seed, and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="randf">
|
<method name="randf">
|
||||||
<return type="float">
|
<return type="float">
|
||||||
</return>
|
</return>
|
||||||
<description>
|
<description>
|
||||||
Random value (0 to 1 float).
|
Return a random floating point value between 0 and 1.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="randi">
|
<method name="randi">
|
||||||
<return type="int">
|
<return type="int">
|
||||||
</return>
|
</return>
|
||||||
<description>
|
<description>
|
||||||
Random 32 bits value (integer). To obtain a value from 0 to N, you can use remainder, like (for random from 0 to 19): randi() % 20.
|
Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use remainder. For example, to get a random integer between 0 and 19 inclusive, you can use randi() % 20.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="randomize">
|
<method name="randomize">
|
||||||
<return type="Nil">
|
<return type="Nil">
|
||||||
</return>
|
</return>
|
||||||
<description>
|
<description>
|
||||||
Reset the seed of the random number generator with a new, different one.
|
Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
<method name="range">
|
<method name="range">
|
||||||
|
|
|
@ -351,14 +351,14 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
|
||||||
case MATH_SEED: {
|
case MATH_SEED: {
|
||||||
VALIDATE_ARG_COUNT(1);
|
VALIDATE_ARG_COUNT(1);
|
||||||
VALIDATE_ARG_NUM(0);
|
VALIDATE_ARG_NUM(0);
|
||||||
uint32_t seed=*p_args[0];
|
uint64_t seed=*p_args[0];
|
||||||
Math::seed(seed);
|
Math::seed(seed);
|
||||||
r_ret=Variant();
|
r_ret=Variant();
|
||||||
} break;
|
} break;
|
||||||
case MATH_RANDSEED: {
|
case MATH_RANDSEED: {
|
||||||
VALIDATE_ARG_COUNT(1);
|
VALIDATE_ARG_COUNT(1);
|
||||||
VALIDATE_ARG_NUM(0);
|
VALIDATE_ARG_NUM(0);
|
||||||
uint32_t seed=*p_args[0];
|
uint64_t seed=*p_args[0];
|
||||||
int ret = Math::rand_from_seed(&seed);
|
int ret = Math::rand_from_seed(&seed);
|
||||||
Array reta;
|
Array reta;
|
||||||
reta.push_back(ret);
|
reta.push_back(ret);
|
||||||
|
|
|
@ -802,14 +802,14 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func,const Variant** p_inp
|
||||||
case VisualScriptBuiltinFunc::MATH_SEED: {
|
case VisualScriptBuiltinFunc::MATH_SEED: {
|
||||||
|
|
||||||
VALIDATE_ARG_NUM(0);
|
VALIDATE_ARG_NUM(0);
|
||||||
uint32_t seed=*p_inputs[0];
|
uint64_t seed=*p_inputs[0];
|
||||||
Math::seed(seed);
|
Math::seed(seed);
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case VisualScriptBuiltinFunc::MATH_RANDSEED: {
|
case VisualScriptBuiltinFunc::MATH_RANDSEED: {
|
||||||
|
|
||||||
VALIDATE_ARG_NUM(0);
|
VALIDATE_ARG_NUM(0);
|
||||||
uint32_t seed=*p_inputs[0];
|
uint64_t seed=*p_inputs[0];
|
||||||
int ret = Math::rand_from_seed(&seed);
|
int ret = Math::rand_from_seed(&seed);
|
||||||
Array reta;
|
Array reta;
|
||||||
reta.push_back(ret);
|
reta.push_back(ret);
|
||||||
|
|
Loading…
Reference in a new issue