Skip to content

Instantly share code, notes, and snippets.

@t-mat
Created April 2, 2021 04:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t-mat/8711416ee535e067343571457a80a718 to your computer and use it in GitHub Desktop.
Save t-mat/8711416ee535e067343571457a80a718 to your computer and use it in GitHub Desktop.
[C++] PCG32 just for me.
// PCG32 just for me.
// Copyright (C) 2021, Takayuki Matsuoka.
// SPDX-License-Identifier: MIT
//
// There's no "stream id". Instead of it, use advance(K).
// For example, if you want to have N streams, you can use the following code:
//
// Pcg32 my_pcg32[N];
// for(int i = 0 ; i < N; ++i) {
// const int64_t k = (INT64_MAX / N) * i;
// my_pcg32[i].init();
// my_pcg32[i].advance(k);
// }
//
// --------
// PCG - Minimal C Implementation
// https://github.com/imneme/pcg-c-basic
//
// *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org
// Licensed under Apache License 2.0 (NO WARRANTY, etc. see website)
// https://en.wikipedia.org/wiki/Permuted_congruential_generator#Example_code
//
// --------
// pcg32 - Tiny self-contained C++ version of the PCG32 pseudorandom number generator
// https://github.com/wjakob/pcg32
//
// Tiny self-contained version of the PCG Random Number Generation for C++
// put together from pieces of the much larger C/C++ codebase.
// Wenzel Jakob, February 2015
// Licensed under the Apache License, Version 2.0
//
// --------
// Permuted congruential generator - Wikipedia
// https://en.wikipedia.org/wiki/Permuted_congruential_generator
//
// Linear congruential generator - Wikipedia
// https://en.wikipedia.org/wiki/Linear_congruential_generator
//
#pragma once
#if !defined(MY_PCG32_HPP_HAS_ROTR) && defined(__has_include) && (__has_include(<bit>))
# include <bit>
# define MY_PCG32_HPP_HAS_ROTR 1
#endif
#include <cstdint>
struct Pcg32 {
void init() {
state = default_state;
}
// Generates a pseudorandom uniformly distributed 32-bit unsigned integer.
//
// Return: 32bit unsigned number, x, where, 0 <= x < 2^32.
uint32_t random() {
const uint64_t x = advance();
const uint32_t r = static_cast<uint32_t>(x >> 59); // 0 <= r <= 31
const uint64_t y = x ^ (x >> 18);
const uint32_t z = static_cast<uint32_t>(y >> 27);
const uint32_t w = rotr32(z, r);
return w;
}
// Single step advance function
//
// Return: Old state (state before advance).
//
// Advance one step forward. For mutistep forward/backward, use advance(int64_t).
uint64_t advance() {
const uint64_t oldState = state;
state = oldState * lcg_mult + lcg_inc; // Advance internal state
return oldState;
}
// Multi-step advance function (jump-ahead, jump-back)
//
// Return: Old state (state before advance).
//
// The method used here is based on Brown, "Random Number Generation with
// Arbitrary Stride", Transactions of the American Nuclear Society (Nov.
// 1994). The algorithm is very similar to fast exponentiation.
uint64_t advance(int64_t delta) {
uint64_t acc_mult = 1u;
uint64_t acc_plus = 0u;
// Even though delta is an unsigned integer, we can pass a signed
// integer to go backwards, it just goes "the long way round".
{
uint64_t cur_mult = lcg_mult;
uint64_t cur_plus = lcg_inc;
for(uint64_t d = static_cast<uint64_t>(delta); d > 0; d /= 2) {
if((d & 1) != 0) {
acc_mult *= cur_mult;
acc_plus = acc_plus * cur_mult + cur_plus;
}
cur_plus = (cur_mult + 1) * cur_plus;
cur_mult *= cur_mult;
}
}
const uint64_t oldState = state;
state = acc_mult * state + acc_plus;
return oldState;
}
// bitwise rotate function for compatibility
uint32_t rotr32(uint32_t x, uint32_t rot) {
#if defined(MY_PCG32_HPP_HAS_ROTR) && (MY_PCG32_HPP_HAS_ROTR != 0)
return std::rotr(x, rot);
#else
return (x >> rot) | (x << ((32 - rot) & 31));
#endif
}
// The current state. The RNG iterates through all 2^64 possible internal states.
uint64_t state = default_state;
static inline const uint64_t lcg_mult = 6364136223846793005; // Adopted from MMIX.
static inline const uint64_t lcg_inc = 0xda3e39cb94b95bdbULL; // Adopted from pcg_basic.h, PCG32_INITIALIZER.
static inline const uint64_t default_state = 0x853c49e6748fea9bULL; // Default state. Adopted from pcg_basic.h, PCG32_INITIALIZER.
};
#if defined(MY_PCG32_HPP_TEST)
#include <stdio.h>
#include <stdlib.h>
#include "pcg_basic.h" // https://github.com/imneme/pcg-c-basic
int main() {
int errcnt = 0;
pcg32_random_t pcg32 = PCG32_INITIALIZER;
Pcg32 mypcg32 {};
Pcg32 my_pcg32[8];
for(int64_t i = 0; i < 1024*1024*1024; ++i) {
const auto r0 = pcg32_random_r(&pcg32);
const auto r1 = mypcg32.random();
if(r0 != r1) {
printf("ERROR\n");
errcnt += 1;
}
}
printf("errcnt = %d\n", errcnt);
exit(errcnt == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment