Created
April 2, 2021 04:57
-
-
Save t-mat/8711416ee535e067343571457a80a718 to your computer and use it in GitHub Desktop.
[C++] PCG32 just for me.
This file contains 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
// 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