Skip to content

Commit 4de7e28

Browse files
authored
Move FastRNG to ax ns and more improvements (#2057)
1 parent 4c80b80 commit 4de7e28

File tree

2 files changed

+56
-75
lines changed

2 files changed

+56
-75
lines changed

core/math/FastRNG.h

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
/****************************************************************************
2-
Copyright (c) 2012 cocos2d-x.org
3-
Copyright (c) 2013-2016 Chukong Technologies Inc.
4-
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
52
Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md).
63
74
https://axmol.dev/
@@ -28,6 +25,12 @@
2825
#ifndef __FAST_RNG_H__
2926
#define __FAST_RNG_H__
3027

28+
#include "math/MathBase.h"
29+
#include <type_traits>
30+
#include <stdint.h>
31+
32+
NS_AX_MATH_BEGIN
33+
3134
/** A fast more effective seeded random number generator struct, uses xoshiro128**.
3235
* It uses a simple algorithm to improve the speed of generating random numbers with a decent quality,
3336
* Use this if you're planning to generate large amounts of random numbers in a single frame.
@@ -36,36 +39,56 @@
3639
*/
3740
struct FastRNG
3841
{
42+
private:
3943
uint32_t s[4];
4044

4145
// SplitMix64 implementation, doesn't modify any state for this instance
4246
// but it is used to seed xoshiro128** state
43-
uint64_t nextSeed(uint64_t& state)
47+
static inline uint64_t nextSeed(uint64_t& state)
4448
{
4549
uint64_t z = (state += 0x9e3779b97f4a7c15);
4650
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
4751
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
4852
return z ^ (z >> 31);
4953
}
5054

55+
// returns a copy of x rotated k bits to the left
56+
static inline uint32_t rotL(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); }
57+
58+
// generates a random integer from 0 to max exclusive that is uniformly distributed using fastrange algorithm
59+
uint32_t nextMax(uint32_t max)
60+
{
61+
uint64_t multiresult = static_cast<uint64_t>(next()) * max;
62+
uint32_t leftover = static_cast<uint32_t>(multiresult);
63+
if (leftover < max)
64+
{
65+
uint32_t threshold = (0 - max) % max;
66+
while (leftover < threshold)
67+
{
68+
multiresult = static_cast<uint64_t>(next()) * max;
69+
leftover = static_cast<uint32_t>(multiresult);
70+
}
71+
}
72+
return multiresult >> 32;
73+
}
74+
75+
public:
5176
FastRNG() { seed(static_cast<uint64_t>(rand()) << 32 | rand()); }
77+
FastRNG(uint64_t _seed) { seed(_seed); }
5278

5379
// there is no need to seed this instance of FastRNG
54-
// because it has already been seeded with rand() by constructor
80+
// because it's already been seeded with rand() in constructor
5581
// you can override the seed by giving your own 64-bit seed
5682
void seed(uint64_t seed)
5783
{
5884
uint64_t state = seed;
5985
uint64_t states[2];
6086
memset(states, 0, 16);
61-
states[0] = nextSeed(state);
62-
states[1] = nextSeed(state);
87+
states[0] = FastRNG::nextSeed(state);
88+
states[1] = FastRNG::nextSeed(state);
6389
memcpy(s, states, 16);
6490
}
6591

66-
// returns a copy of x rotated k bits to the left
67-
static inline uint32_t rotL(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); }
68-
6992
// steps once into the state, returns a random from 0 to UINT32_MAX
7093
uint32_t next()
7194
{
@@ -89,11 +112,12 @@ struct FastRNG
89112
template <typename T>
90113
T nextReal()
91114
{
92-
if (std::is_same<T, float>::value)
115+
if constexpr (std::is_same<T, float>::value)
93116
return static_cast<T>(next() >> 8) * 0x1.0p-24f;
94-
else if (std::is_same<T, double>::value)
117+
else if constexpr (std::is_same<T, double>::value)
95118
return static_cast<T>((static_cast<uint64_t>(next()) << 32 | next()) >> 11) * 0x1.0p-53;
96-
return 0; // possibly assert?
119+
else
120+
AXASSERT(false, "datatype not implemented.");
97121
}
98122

99123
// generates a random real that ranges from min to max
@@ -110,23 +134,6 @@ struct FastRNG
110134
return min + static_cast<T>(nextMax(static_cast<uint32_t>(max - min)));
111135
}
112136

113-
// generates a random integer from 0 to max exclusive that is uniformly distributed using fastrange algorithm
114-
uint32_t nextMax(uint32_t max)
115-
{
116-
uint64_t multiresult = static_cast<uint64_t>(next()) * max;
117-
uint32_t leftover = static_cast<uint32_t>(multiresult);
118-
if (leftover < max)
119-
{
120-
uint32_t threshold = (0 - max) % max;
121-
while (leftover < threshold)
122-
{
123-
multiresult = static_cast<uint64_t>(next()) * max;
124-
leftover = static_cast<uint32_t>(multiresult);
125-
}
126-
}
127-
return multiresult >> 32;
128-
}
129-
130137
// wrapper for nextInt<int32_t>(min, max)
131138
int32_t range(int32_t min, int32_t max) { return nextInt<int32_t>(min, max); }
132139
// wrapper for nextInt<int32_t>(0, max)
@@ -150,9 +157,11 @@ struct FastRNG
150157
// wrapper for nextReal<float>()
151158
float float01() { return nextReal<float>(); }
152159
// wrapper for nextReal<double>()
153-
float double01() { return nextReal<double>(); }
160+
double double01() { return nextReal<double>(); }
154161
// wrapper for next() & 1, true or false based on LSB
155-
float bool01() { return next() & 1; }
162+
bool bool01() { return static_cast<bool>(next() & 1); }
156163
};
157164

165+
NS_AX_MATH_END
166+
158167
#endif // __FAST_RNG_H__

tests/unit-tests/Source/core/math/FastRNGTests.cpp

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -29,33 +29,15 @@
2929

3030

3131
TEST_SUITE("math/FastRNG") {
32-
TEST_CASE("nextSeed")
33-
{
34-
auto rng = FastRNG();
35-
36-
uint64_t s = 0x1234'5678'9ABC'EFFF;
37-
CHECK_EQ(2299331620237437860u, rng.nextSeed(s));
38-
CHECK_EQ(8718988738428180276u, rng.nextSeed(s));
39-
}
40-
41-
TEST_CASE("rotL")
42-
{
43-
auto rng = FastRNG();
44-
45-
uint32_t s = 0x1357'9BDF;
46-
CHECK_EQ(2939665958, rng.rotL(s, 9));
47-
CHECK_EQ(4084982378, rng.rotL(s, 13));
48-
}
49-
5032
TEST_CASE("next") {
51-
auto rng = FastRNG();
33+
auto rng = ax::FastRNG();
5234
rng.seed(1);
5335

5436
CHECK_EQ(1695105466, rng.next());
5537
CHECK_EQ(1423115009, rng.next());
5638
CHECK_EQ(634581793, rng.next());
5739

58-
auto rng2 = FastRNG();
40+
auto rng2 = ax::FastRNG();
5941
rng2.seed(2);
6042

6143
CHECK_EQ(1086064458, rng2.next());
@@ -65,7 +47,7 @@ TEST_SUITE("math/FastRNG") {
6547

6648
TEST_CASE("nextInt")
6749
{
68-
auto rng = FastRNG();
50+
auto rng = ax::FastRNG();
6951
rng.seed(12);
7052

7153
CHECK_EQ(30, rng.nextInt<int8_t>(INT8_MIN, INT8_MAX));
@@ -80,7 +62,7 @@ TEST_SUITE("math/FastRNG") {
8062

8163
TEST_CASE("nextReal")
8264
{
83-
auto rng = FastRNG();
65+
auto rng = ax::FastRNG();
8466
rng.seed(14);
8567

8668
CHECK_EQ(doctest::Approx(0.927014), rng.nextReal<float>());
@@ -92,18 +74,8 @@ TEST_SUITE("math/FastRNG") {
9274
CHECK_EQ(doctest::Approx(9.94378e+307), rng.nextReal<double>(DBL_MIN, DBL_MAX));
9375
}
9476

95-
TEST_CASE("nextMax")
96-
{
97-
auto rng = FastRNG();
98-
rng.seed(16);
99-
100-
CHECK_EQ(1381652921, rng.nextMax(UINT32_MAX));
101-
CHECK_EQ(46435, rng.nextMax(UINT16_MAX));
102-
CHECK_EQ(84, rng.nextMax(UINT8_MAX));
103-
}
104-
10577
TEST_CASE("range") {
106-
auto rng = FastRNG();
78+
auto rng = ax::FastRNG();
10779
rng.seed(12345);
10880

10981
CHECK_EQ(0, rng.range(0, 1));
@@ -119,7 +91,7 @@ TEST_SUITE("math/FastRNG") {
11991

12092
TEST_CASE("max")
12193
{
122-
auto rng = FastRNG();
94+
auto rng = ax::FastRNG();
12395
rng.seed(1);
12496

12597
CHECK_EQ(0, rng.max(1));
@@ -129,7 +101,7 @@ TEST_SUITE("math/FastRNG") {
129101

130102

131103
TEST_CASE("rangeu") {
132-
auto rng = FastRNG();
104+
auto rng = ax::FastRNG();
133105
rng.seed(1);
134106

135107
CHECK_EQ(0u, rng.rangeu(0u, 1u));
@@ -141,7 +113,7 @@ TEST_SUITE("math/FastRNG") {
141113

142114
TEST_CASE("maxu")
143115
{
144-
auto rng = FastRNG();
116+
auto rng = ax::FastRNG();
145117
rng.seed(1);
146118

147119
CHECK_EQ(0u, rng.maxu(1));
@@ -150,7 +122,7 @@ TEST_SUITE("math/FastRNG") {
150122
}
151123

152124
TEST_CASE("rangef") {
153-
auto rng = FastRNG();
125+
auto rng = ax::FastRNG();
154126
rng.seed(1);
155127

156128
CHECK_EQ(doctest::Approx(-0.210655), rng.rangef(-1.0f, 1.0f));
@@ -163,7 +135,7 @@ TEST_SUITE("math/FastRNG") {
163135

164136

165137
TEST_CASE("maxf") {
166-
auto rng = FastRNG();
138+
auto rng = ax::FastRNG();
167139
rng.seed(1);
168140

169141
CHECK_EQ(doctest::Approx(0.394672), rng.maxf(1.0f));
@@ -175,7 +147,7 @@ TEST_SUITE("math/FastRNG") {
175147

176148
TEST_CASE("ranged")
177149
{
178-
auto rng = FastRNG();
150+
auto rng = ax::FastRNG();
179151
rng.seed(1);
180152

181153
CHECK_EQ(doctest::Approx(-0.210655), rng.ranged(-1.0, 1.0));
@@ -185,7 +157,7 @@ TEST_SUITE("math/FastRNG") {
185157

186158
TEST_CASE("maxd")
187159
{
188-
auto rng = FastRNG();
160+
auto rng = ax::FastRNG();
189161
rng.seed(1);
190162

191163
CHECK_EQ(doctest::Approx(0.394672), rng.maxd(1.0f));
@@ -196,7 +168,7 @@ TEST_SUITE("math/FastRNG") {
196168
}
197169

198170
TEST_CASE("float01") {
199-
auto rng = FastRNG();
171+
auto rng = ax::FastRNG();
200172
rng.seed(1);
201173

202174
CHECK_EQ(doctest::Approx(0.394672), rng.float01());
@@ -207,7 +179,7 @@ TEST_SUITE("math/FastRNG") {
207179

208180
TEST_CASE("double01")
209181
{
210-
auto rng = FastRNG();
182+
auto rng = ax::FastRNG();
211183
rng.seed(1);
212184

213185
CHECK_EQ(doctest::Approx(0.394672), rng.double01());
@@ -217,7 +189,7 @@ TEST_SUITE("math/FastRNG") {
217189
}
218190

219191
TEST_CASE("bool01") {
220-
auto rng = FastRNG();
192+
auto rng = ax::FastRNG();
221193
rng.seed(1);
222194

223195
auto t = 0;

0 commit comments

Comments
 (0)