Skip to content

Commit ef169f5

Browse files
authored
[libc] Improve the implementation of the rand() function (#66131)
Summary: This patch improves the implementation of the standard `rand()` function by implementing it in terms of the xorshift64star pRNG as described in https://en.wikipedia.org/wiki/Xorshift#xorshift*. This is a good, general purpose random number generator that is sufficient for most applications that do not require an extremely long period. This patch also correctly initializes the seed to be `1` as described by the standard. We also increase the `RAND_MAX` value to be `INT_MAX` as the standard only specifies that it can be larger than 32768.
1 parent d671126 commit ef169f5

File tree

4 files changed

+21
-7
lines changed

4 files changed

+21
-7
lines changed

libc/include/llvm-libc-macros/stdlib-macros.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
#define EXIT_SUCCESS 0
1818
#define EXIT_FAILURE 1
1919

20-
#define RAND_MAX 32767
20+
#define RAND_MAX 2147483647
2121

2222
#endif // __LLVM_LIBC_MACROS_STDLIB_MACROS_H

libc/src/stdlib/rand.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212

1313
namespace __llvm_libc {
1414

15-
// This rand function is the example implementation from the C standard. It is
16-
// not cryptographically secure.
17-
LLVM_LIBC_FUNCTION(int, rand, (void)) { // RAND_MAX is assumed to be 32767
18-
rand_next = rand_next * 1103515245 + 12345;
19-
return static_cast<unsigned int>((rand_next / 65536) % 32768);
15+
// An implementation of the xorshift64star pseudo random number generator. This
16+
// is a good general purpose generator for most non-cryptographics applications.
17+
LLVM_LIBC_FUNCTION(int, rand, (void)) {
18+
rand_next ^= rand_next >> 12;
19+
rand_next ^= rand_next << 25;
20+
rand_next ^= rand_next >> 27;
21+
return static_cast<int>((rand_next * 0x2545F4914F6CDD1Dul) >> 32) & RAND_MAX;
2022
}
2123

2224
} // namespace __llvm_libc

libc/src/stdlib/rand_util.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace __llvm_libc {
1313

14-
LIBC_THREAD_LOCAL unsigned long rand_next;
14+
// C standard 7.10p2: If 'rand' is called before 'srand' it is to proceed as if
15+
// the 'srand' function was called with a value of '1'.
16+
LIBC_THREAD_LOCAL unsigned long rand_next = 1;
1517

1618
} // namespace __llvm_libc

libc/test/src/stdlib/rand_test.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,21 @@
1414
#include <stdlib.h>
1515

1616
TEST(LlvmLibcRandTest, UnsetSeed) {
17+
static int vals[1000];
18+
1719
for (size_t i = 0; i < 1000; ++i) {
1820
int val = __llvm_libc::rand();
1921
ASSERT_GE(val, 0);
2022
ASSERT_LE(val, RAND_MAX);
23+
vals[i] = val;
2124
}
25+
26+
// The C standard specifies that if 'srand' is never called it should behave
27+
// as if 'srand' was called with a value of 1. If we seed the value with 1 we
28+
// should get the same sequence as the unseeded version.
29+
__llvm_libc::srand(1);
30+
for (size_t i = 0; i < 1000; ++i)
31+
ASSERT_EQ(__llvm_libc::rand(), vals[i]);
2232
}
2333

2434
TEST(LlvmLibcRandTest, SetSeed) {

0 commit comments

Comments
 (0)