Skip to content

Instantly share code, notes, and snippets.

@MajsterTynek
Last active May 5, 2022 12:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MajsterTynek/0940dfcf1ffe80e3e5517ac1f46ca42a to your computer and use it in GitHub Desktop.
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++.
#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
#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;
}
#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