Last active
May 5, 2022 12:51
-
-
Save MajsterTynek/0940dfcf1ffe80e3e5517ac1f46ca42a to your computer and use it in GitHub Desktop.
C implementation of 'java.lang.Random' using preprocessor macros, without using structures or classes. Use-cases were [tested](imgur.com/a/SN9ZP0B) with C++.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef java_util_Random | |
#define java_util_Random | |
/// this mimics the java.util.Random class using C language | |
/// ensure thread safety on your own as there is no synchronization | |
/// JavaRandom rand = MAKESEED(seed); // | |
typedef long long JavaRandom; // no need for classes! | |
/// for default constructor feed it millis since epoch | |
#define MAKESEED(seed) (((seed) ^ 0x5DEECE66DLL) & ((1LL << 48) - 1)) | |
#define UPDATESEED(seed) (((seed) * 0x5DEECE66DLL + 0xBLL) & ((1LL << 48) - 1)) | |
/// macro of int java.util.Random.next(int bits) | |
#define NEXTBITS(seed, bits) ( \ | |
((seed) = UPDATESEED(seed)), \ | |
(int)((unsigned long long)(seed) >> (48 - (bits)))) | |
/// macro of int java.util.Random.nextInt() | |
#define NEXTRAND(seed) (NEXTBITS((seed), 32)) | |
/// macro of int java.util.Random.nextBoolean() | |
#define NEXTBOOL(seed) ((bool)NEXTBITS((seed), 1)) | |
// fix for -Wsequence-point warning and wrong results | |
static inline long long __nxtll(JavaRandom &rand) { | |
long long bits = NEXTRAND(rand); // pretty simple | |
return (bits << 32) | (unsigned int) NEXTRAND(rand); | |
} | |
// fix for -Wsequence-point warning and wrong results | |
static inline double __nxtd(JavaRandom &rand) { | |
long long bits = ((long long)NEXTBITS(rand, 26) << 27); | |
bits |= (long long)NEXTBITS(rand, 27); | |
return bits / (double)(1LL << 53); // 0x1.0p-53 ? | |
} | |
/// macro of int java.util.Random.nextLong() | |
#define NEXTLONG(seed) (__nxtll(seed)) | |
/// macro of int java.util.Random.nextFloat() | |
#define NEXTFLOAT(seed) (NEXTBITS(seed, 24) / (float)(1 << 24)) | |
/// macro of int java.util.Random.nextDouble() | |
#define NEXTDOUBLE(seed) (__nxtd(seed)) | |
#ifdef NETWORK_BYTE_ORDER | |
// in case of big endian machine | |
#include <immintrin.h> | |
#define __bswap32 _bswap | |
#else // no swap on little nedian | |
#define __bswap32(i) (i) | |
#endif // NETWORK_BYTE_ORDER | |
#include <stddef.h> | |
/// replacement of java.util.Random.nextBytes() | |
/// assumed little endian, meaning easy byte swap by memory store | |
static inline void nextBytes(JavaRandom &rand, char *arr, size_t bytes) { | |
JavaRandom r = rand; | |
size_t ints = bytes / 4; | |
for (size_t b = 0; b < ints; b++) | |
((int*)arr)[b] = __bswap32(NEXTRAND(r)); | |
if (ints * 4 != bytes) { // if not multiple of 4 | |
unsigned int last = NEXTRAND(r); | |
for (size_t b = ints * 4; b < bytes; b++) | |
arr[b] = last & 0xFF, last >>= 8; | |
} | |
rand = r; | |
} | |
#include <math.h> | |
/// replacement of java.util.Random.nextGaussian() | |
/// you should save second result as it may be used out of sequence | |
/// originally there was boolean field named 'haveNextNextGaussian' | |
static inline void nextGaussianPair(JavaRandom &rand, double &x, double &y) { | |
JavaRandom r = rand; | |
double a, b, s; // See Knuth, ACP, Section 3.4.1 Algorithm C. | |
do { | |
a = 2 * NEXTDOUBLE(r) - 1; // Between -1.0 and 1.0. | |
b = 2 * NEXTDOUBLE(r) - 1; // Between -1.0 and 1.0. | |
s = a * a + b * b; | |
} while (s >= 1); | |
double norm = sqrt(-2 * log(s) / s); | |
rand = r, x = a * norm, y = b * norm; | |
} | |
/// replacement of java.util.Random.nextInt(int bound) | |
static inline int nextBoundedInt(JavaRandom &oldrand, int bound) { | |
JavaRandom rand = oldrand; | |
int r = NEXTBITS(rand, 31); | |
int m = bound - 1; | |
if ((bound & m) == 0) // i.e., bound is a power of 2 | |
r = (int)((bound * (long long)r) >> 31); | |
else { | |
for (int u = r; | |
u - (r = u % bound) + m < 0; | |
u = NEXTBITS(rand, 31)) | |
; | |
} | |
oldrand = rand; | |
return r; | |
} | |
#endif // java_util_Random |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdlib> | |
#include <iostream> | |
#include <iomanip> | |
//#define NETWORK_BYTE_ORDER | |
#include "JavaRandom.h" | |
#define HEXFLOAT(x) '\t' << std::hexfloat \ | |
<< x << std::defaultfloat << '\n' | |
/// test suite for JavaRandom | |
int main(int argc, char* argv[]) { | |
if (argc > 2) return 1; | |
long long seed = std::atoll(argv[1]); | |
JavaRandom rand = MAKESEED(seed); | |
std::cout << "Seed used:\t" << seed << std::endl; | |
std::cout << "Internal:\t" << rand << std::endl; | |
std::cout << std::setprecision(7) << std::endl; | |
int n = NEXTRAND(rand); | |
long long l = NEXTLONG(rand); | |
float f = NEXTFLOAT(rand); | |
double d = NEXTDOUBLE(rand); | |
std::cout << "nextInt " << n << '\n'; | |
std::cout << "nextLong " << l << '\n'; | |
std::cout << "nextFloat " << f << HEXFLOAT(f); | |
std::cout << "nextDouble " << d << HEXFLOAT(d); | |
double x, y, a, b; | |
rand = MAKESEED(seed); // restart | |
nextGaussianPair(rand, x, y); | |
nextGaussianPair(rand, a, b); | |
std::cout << std::endl; | |
std::cout << "Gaussian X: " << x << HEXFLOAT(x); | |
std::cout << "Gaussian Y: " << y << HEXFLOAT(y); | |
std::cout << "Gaussian A: " << a << HEXFLOAT(a); | |
std::cout << "Gaussian B: " << b << HEXFLOAT(b); | |
std::cout << std::endl; | |
rand = MAKESEED(seed); // test long run for floats | |
for (int i = 6662137; i > 1; i--) NEXTFLOAT(rand); // skip | |
float farFloat = NEXTFLOAT(rand); // gotta print twice | |
std::cout << "farFloat " << farFloat << HEXFLOAT(farFloat); | |
rand = MAKESEED(seed); // test long run for doubles | |
for (int i = 6662137; i > 1; i--) NEXTDOUBLE(rand); // skip | |
double farDouble = NEXTDOUBLE(rand); // gotta print twice | |
std::cout << "farDouble " << farDouble << HEXFLOAT(farDouble); | |
rand = MAKESEED(seed); // test for long longs in long run | |
for (int i = 6662137; i > 1; i--) NEXTLONG(rand); // skip | |
std::cout << "farLong " << NEXTLONG(rand) << std::endl; | |
rand = MAKESEED(seed); // test long run for ints | |
for (int i = 6662137; i > 1; i--) NEXTRAND(rand); // skip | |
std::cout << "farInt " << NEXTRAND(rand) << std::endl; | |
char arr[11] = {0}; | |
rand = MAKESEED(seed); // test byte array filling | |
nextBytes(rand, arr, sizeof(arr)); | |
std::cout << "\nnextBytes: "; | |
for (size_t i = 0; i < sizeof(arr); i++) | |
std::cout << int(arr[i]) << ' '; | |
std::cout << std::endl; | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdlib> | |
#include <iostream> | |
#include <iomanip> | |
//#define NETWORK_BYTE_ORDER | |
#include "JavaRandom.h" | |
/// test of method implementation | |
/// java.util.Random.nextInt(int bound) | |
int main(int argc, char* argv[]) { | |
if (argc > 2) return 1; // defaults to 0 | |
long long seed = std::atoll(argv[1]); | |
JavaRandom rand = MAKESEED(seed); | |
std::cout << "Seed used:\t" << seed << std::endl; | |
std::cout << "Internal:\t" << rand << std::endl; | |
for (int i = 1; i <= 10; i++) | |
std::cout << "boundedInt " << i << '\t' | |
<< nextBoundedInt(rand, 10000) << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment