Published .

pcg64.tar

pcg64.c

#include "pcg64.h"

// This implementation is by Jonas Hvid, 2025, based in part on the original C++
// and C implementations by Melissa O'Neill, 2019. The latter are each released
// under the Apache License, Version 2.0 or the MIT license. To the extent
// possible, I dedicate the following code to the public domain. For more
// information about copyright, see https://www.pcg-random.org/download.html.

// For compilation instructions, see header file.

#include <stdint.h>
#include <stdlib.h>
#include <sys/random.h>

static void pcg64_step(pcg64_t *g) {
  uint128_t m = ((uint128_t)0x2360ED051FC65DA4 << 64) + 0x4385DF649FCCF645;
  g->s = g->s * m + g->q;
}

pcg64_t pcg64_init_values(uint128_t state, uint128_t seq) {
  pcg64_t g;
  g.s = 0;
  g.q = (seq << 1u) | 1u;
  pcg64_step(&g);
  g.s += state;
  pcg64_step(&g);
  return g;
}

pcg64_t pcg64_init_getrandom() {
  pcg64_t g;
  if (getrandom(&g.s, sizeof(uint128_t), 0) != sizeof(uint128_t)) exit(1);
  if (getrandom(&g.q, sizeof(uint128_t), 0) != sizeof(uint128_t)) exit(1);
  return pcg64_init_values(g.s, g.q);
}

static uint64_t pcg64_output(uint128_t s) {
  // PCG-DXSM
  uint64_t h = s >> 64;
  uint64_t l = s;
  l |= 1;
  h ^= h >> 32;
  h *= 0xDA942042E4DD58B5ull;
  h ^= h >> 48;
  h *= l;
  return h;
}

uint64_t pcg64_random(pcg64_t *g) {
  pcg64_step(g);
  return pcg64_output(g->s);
}

uint64_t pcg64_bounded(pcg64_t *g, uint64_t n) {
  uint64_t threshold = -n % n;
  while (1) {
    uint64_t r = pcg64_random(g);
    if (r >= threshold) {
      return r % n;
    }
  }
}

pcg64.h

#ifndef PCG64_H
#define PCG64_H

// Build and use shared library:
//   gcc -std=c23 -c -fpic -o pcg64.o pcg64.c
//   gcc -shared -o libpcg64.so pcg64.o
//   gcc -lpcg64 -o main main.c
//
// Link statically:
//   gcc -std=c23 -c -o pcg64.o pcg64.c
//   gcc -o main main.c pcg64.o

// This implementation depends on native support for __int128.

// For copyright, see implementation file.

#include <stdint.h>

typedef unsigned __int128 uint128_t;
typedef struct pcg64_t { uint128_t s; uint128_t q; } pcg64_t;

pcg64_t pcg64_init_values(uint128_t state, uint128_t seq);
pcg64_t pcg64_init_getrandom();
uint64_t pcg64_random(pcg64_t *);
uint64_t pcg64_bounded(pcg64_t *, uint64_t n);

#endif

demo.c

#include <stdio.h>
#include "pcg64.h"

int main() {
  pcg64_t state = pcg64_init_getrandom();
  for (int i = 0; i < 16; ++i) {
    printf("%02lX ", pcg64_bounded(&state, 0x100));
  }
  printf("\n");
  return 0;
}

Makefile

CF = -std=c23 -Wall -Wextra -Os
LF = -s
C  = gcc

demo-static: demo.c pcg64.c
	$(C) $(CF) $(LF) -o $@ $^

demo-dynamic: demo.c libpcg64.so
	$(C) $(CF) $(LF) -L. -lpcg64 -Wl,-rpath,'$$ORIGIN' -o $@ $<

libpcg64.so: pcg64.c
	$(C) $(CF) $(LF) -fpic -shared -o $@ $<

.PHONY: clean
clean:
	rm -fv *.o *.so demo-{static,dynamic}