Browse Source

Add random::get<T>() functions.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
00d8f899d5
2 changed files with 84 additions and 75 deletions
  1. +79
    -36
      include/random.hpp
  2. +5
    -39
      src/random.cpp

+ 79
- 36
include/random.hpp View File

@@ -16,7 +16,6 @@ Example:
std::random_device rd;
random::Xoroshiro128Plus rng(rd(), rd());
uint64_t r = rng();
uint32_t r = rng.u32();

std::uniform_real_distribution<float> uniform(0.f, 1.f);
float r = uniform(rng);
@@ -59,31 +58,10 @@ struct Xoroshiro128Plus {
constexpr uint64_t max() const {
return UINT64_MAX;
}

uint64_t u64() {
return operator()();
}
uint64_t u32() {
// Take top 32 bits which has better randomness properties.
return u64() >> 32;
}
uint16_t u16() {
return u64() >> 48;
}
uint8_t u8() {
return u64() >> 56;
}
float f32() {
// The multiplier is 2f7fffff in hex. This gives maximum precision of uint32_t -> float conversion and its image is [0, 1).
return u32() * 2.32830629e-10f;
}
float f64() {
return u64() * 5.421010862427522e-20;
}
};


// Simple random API
// Simple thread-local API

/** Initializes the thread-local RNG state.
Must call when creating a thread, otherwise random state is undefined (might always return 0).
@@ -92,26 +70,91 @@ void init();

/** Returns the thread-local generator.
*/
Xoroshiro128Plus& get();
Xoroshiro128Plus& local();

/** Returns a uniform random uint64_t from 0 to UINT64_MAX */
inline uint64_t u64() {
return get().u64();
template <typename T>
T get() {
// Call operator()() and cast by default
return local()();
}
/** Returns a uniform random uint32_t from 0 to UINT32_MAX */
inline uint32_t u32() {
return get().u32();

template <>
inline uint32_t get() {
// Take top 32 bits which has better randomness properties.
return get<uint64_t>() >> 32;
}
/** Returns a uniform random float in the interval [0.0, 1.0) */
inline float uniform() {
return get().f32();

template <>
inline uint16_t get() {
return get<uint64_t>() >> 48;
}

template <>
inline uint8_t get() {
return get<uint64_t>() >> 56;
}

template <>
inline bool get() {
return get<uint64_t>() >> 63;
}

template <>
inline float get() {
// The multiplier is 2f7fffff in hex. This gives maximum precision of uint32_t -> float conversion and its image is [0, 1).
return get<uint32_t>() * 2.32830629e-10f;
}

template <>
inline double get() {
return get<uint64_t>() * 5.421010862427522e-20;
}


/** Returns a uniform random uint64_t from 0 to UINT64_MAX */
inline uint64_t u64() {return get<uint64_t>();}
/** Returns a uniform random uint32_t from 0 to UINT32_MAX */
inline uint32_t u32() {return get<uint32_t>();}
/** Returns a uniform random float in the interval [0.0, 1.0) */
inline float uniform() {return get<float>();}

/** Returns a normal random number with mean 0 and standard deviation 1 */
float normal();
inline float normal() {
// Box-Muller transform
float radius = std::sqrt(-2.f * std::log(1.f - get<float>()));
float theta = 2.f * M_PI * get<float>();
return radius * std::sin(theta);

// // Central Limit Theorem
// const int n = 8;
// float sum = 0.0;
// for (int i = 0; i < n; i++) {
// sum += get<float>();
// }
// return (sum - n / 2.f) / std::sqrt(n / 12.f);
}

/** Fills an array with random bytes. */
void buffer(uint8_t* out, size_t len);
inline void buffer(uint8_t* out, size_t len) {
Xoroshiro128Plus& rng = local();
for (size_t i = 0; i < len; i += 4) {
uint64_t r = rng();
out[i] = r;
if (i + 1 < len)
out[i + 1] = r >> 8;
if (i + 2 < len)
out[i + 2] = r >> 16;
if (i + 3 < len)
out[i + 3] = r >> 24;
}
}

/** Creates a vector of random bytes. */
std::vector<uint8_t> vector(size_t len);
inline std::vector<uint8_t> vector(size_t len) {
std::vector<uint8_t> v(len);
buffer(v.data(), len);
return v;
}


} // namespace random


+ 5
- 39
src/random.cpp View File

@@ -20,9 +20,12 @@ void init() {
if (rng.isSeeded())
return;

// Get epoch time in microseconds for seed
struct timeval tv;
gettimeofday(&tv, NULL);
rng.seed(uint64_t(tv.tv_sec) * 1000 * 1000 + tv.tv_usec, threadCounter++);
uint64_t usec = uint64_t(tv.tv_sec) * 1000 * 1000 + tv.tv_usec;
// Add number of initialized threads so far to random seed, so two threads don't get the same seed if initialized at the same time.
rng.seed(usec, threadCounter++);
// Shift state a few times due to low seed entropy
for (int i = 0; i < 4; i++) {
rng();
@@ -30,47 +33,10 @@ void init() {
}


Xoroshiro128Plus& get() {
Xoroshiro128Plus& local() {
return rng;
}


float normal() {
// Box-Muller transform
float radius = std::sqrt(-2.f * std::log(1.f - uniform()));
float theta = 2.f * M_PI * uniform();
return radius * std::sin(theta);

// // Central Limit Theorem
// const int n = 8;
// float sum = 0.0;
// for (int i = 0; i < n; i++) {
// sum += uniform();
// }
// return (sum - n / 2.f) / std::sqrt(n / 12.f);
}


void buffer(uint8_t* out, size_t len) {
for (size_t i = 0; i < len; i += 4) {
uint64_t r = u64();
out[i] = r;
if (i + 1 < len)
out[i + 1] = r >> 8;
if (i + 2 < len)
out[i + 2] = r >> 16;
if (i + 3 < len)
out[i + 3] = r >> 24;
}
}


std::vector<uint8_t> vector(size_t len) {
std::vector<uint8_t> v(len);
buffer(v.data(), len);
return v;
}


} // namespace random
} // namespace rack

Loading…
Cancel
Save