OXYGENHUB
◆ main openmsx / src / utils / random.hh
3.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef RANDOM_HH
2
#define RANDOM_HH
3
4
#include <random>
5
6
/** Return reference to a (shared) global random number generator.
7
  */
8
[[nodiscard]] inline auto& global_urng()
9
{
10
  static std::minstd_rand0 u;
11
  return u;
12
}
13
14
/** Seed the (shared) random number generator.
15
  */
16
inline void randomize()
17
{
18
  static std::random_device rd;
19
  global_urng().seed(rd());
20
}
21
22
/** Return a random boolean value.
23
  */
24
[[nodiscard]] inline bool random_bool()
25
{
26
  // Note: this is only 100% uniform if 'generator.max() -
27
  // generator().min() + 1' is even. This is the case for
28
  // std::minstd_rand0.
29
  auto& generator = global_urng();
30
  return generator() & 1;
31
}
32
33
/** Return a random integer in the range [from, thru] (note: closed interval).
34
  * This function is convenient if you only need a few random values. If you
35
  * need a large amount it's a bit faster to create a local distribution
36
  * object and reuse that for all your values.
37
  */
38
[[nodiscard]] inline int random_int(int from, int thru)
39
{
40
  static std::uniform_int_distribution<int> d;
41
  using parm_t = decltype(d)::param_type;
42
  return d(global_urng(), parm_t{from, thru});
43
}
44
45
/** Return a random float in the range [from, upto) (note: half-open interval).
46
  * This function is convenient if you only need a few random values. If you
47
  * need a large amount it's a bit faster to create a local distribution
48
  * object and reuse that for all your values.
49
  */
50
[[nodiscard]] inline float random_float(float from, float upto)
51
{
52
  static std::uniform_real_distribution<float> d;
53
  using parm_t = decltype(d)::param_type;
54
  return d(global_urng(), parm_t{from, upto});
55
}
56
57
/** Return a random 32-bit value.
58
  * This function should rarely be used. It should NOT be used if you actually
59
  * need random values in a smaller range than [0 .. 0xffffffff]. For example:
60
  * 'random_32bit % N' with
61
  *  - N not a power-of-2: is NOT a uniform distribution anymore (higher values
62
  *     have a slightly lower probability than the lower values)
63
  *  - N a power-of-2: does more work than needed (typically has to call the
64
  *     underlying generator more than once to make sure all 32 bits are
65
  *     random, but then those upper bits are discarded).
66
  */
67
[[nodiscard]] inline uint32_t random_32bit()
68
{
69
  static std::uniform_int_distribution<uint32_t> d;
70
  using parm_t = decltype(d)::param_type;
71
  return d(global_urng(), parm_t{0, 0xffffffff});
72
}
73
74
75
// Compile-time random numbers
76
77
// Constexpr "Permuted Congruential Generator" (PCG)
78
//   generated random numbers at compile-time
79
//   (or it shifts the problem to obtaining a random seed at compile time)
80
// Based upon:
81
//   C++ Weekly - Ep 44 - constexpr Compile Time Random
82
//   https://www.youtube.com/watch?v=rpn_5Mrrxf8
83
template<uint64_t SEED>
84
struct PCG
85
{
86
	constexpr uint32_t operator()()
87
	{
88
		// Advance internal state
89
		auto oldState = state;
90
		state = oldState * 6364136223846793005ULL + (SEED | 1);
91
92
		// Calculate output function (XSH RR), uses old state for max
93
		// ILP
94
		auto xorShifted = uint32_t(((oldState >> 18) ^ oldState) >> 27);
95
		auto rot = int(oldState >> 59);
96
		return (xorShifted >> rot) | (xorShifted << ((-rot) & 31));
97
	}
98
99
private:
100
	uint64_t state = SEED;
101
};
102
103
// Turns a random 32-bit integer into a random float in the range [0, 1).
104
[[nodiscard]] constexpr float getCanonicalFloat(uint32_t u)
105
{
106
	uint32_t b = 1 << 23;
107
	uint32_t m = b - 1;
108
	return float(u & m) / float(b);
109
}
110
111
#endif