Skip to content

Instantly share code, notes, and snippets.

@rawiriblundell
Last active June 8, 2026 00:34
Show Gist options
  • Select an option

  • Save rawiriblundell/d54ca69a61bd28d91c803bffa67cca61 to your computer and use it in GitHub Desktop.

Select an option

Save rawiriblundell/d54ca69a61bd28d91c803bffa67cca61 to your computer and use it in GitHub Desktop.
An implementation of a xorshift128+ RNG in shell code
#!/bin/bash
#set -x
# Function to generate a reliable seed for whatever method requires one
# Because 'date +%s' isn't entirely portable, we try other methods to get the epoch
generate_seed() {
# First we check if /dev/urandom is available.
if [ -c /dev/urandom ] && command -v od >/dev/null 2>&1; then
# Get a string of bytes from /dev/urandom using od
od -N 4 -A n -t uL /dev/urandom | tr -d '\n' | awk '{$1=$1+0};1'
# Otherwise, we see if 'date' will emit an epoch timestamp
elif date '+%s' >/dev/null 2>&1; then
date '+%s'
# In the rare case that we have a limited version of 'date'
# We generate an epoch timestamp with octal correction
# Based on http://www.etalabs.net/sh_tricks.html
else
local int_secs int_mins int_hours int_days int_years int_yearoffset
int_secs=$(TZ=GMT0 date +%S)
int_mins=$(TZ=GMT0 date +%M)
int_hours=$(TZ=GMT0 date +%H)
int_days=$(TZ=GMT0 date +%j | sed 's/^0*//')
int_yearoffset=$(( $(TZ=GMT0 date +%Y) - 1600 ))
int_years=$(( (
int_yearoffset * 365
+ int_yearoffset / 4
- int_yearoffset / 100
+ int_yearoffset / 400
+ int_days
- 135140
) * 86400 ))
printf '%s\n' "$(( \
int_years \
+ (${int_hours#0} * 3600) \
+ (${int_mins#0} * 60) \
+ ${int_secs#0} \
))"
fi
}
# Start assembling our vars
int_count="${1:-1}"
int_min="${2:-1}"
# Figure out our default maxRand, using 'getconf'
case "$(getconf LONG_BIT)" in
(64)
rand_ceiling=9223372036854775807
;;
(32)
rand_ceiling=2147483647
;;
(*)
rand_ceiling=32767
;;
esac
int_max="${3:-$rand_ceiling}"
rand_range=$(( int_max - int_min + 1 ))
(( rand_range > 0 )) || {
printf -- '%s\n' "Invalid range" >&2
exit 1
}
full_range=0
if (( int_min == 0 && int_max == rand_ceiling )); then
full_range=1
else
threshold=$(( (rand_ceiling - rand_range + 1) % rand_range ))
fi
# Initialise the seed state
seed="${4:-$(generate_seed)}"
(( seed == 0 )) && seed=4 # RFC 1149.5
seed0="${seed}"
seed1=$(( seed ^ 0x9e3779b9 ))
# Get the ints ready for the loop
int0="${seed0}"
int1="${seed1}"
while (( int_count > 0 )); do
old_int0="${int0}"
old_int1="${int1}"
# xorshift128+ state transition
int0="${old_int1}"
old_int0=$(( old_int0 ^ (old_int0 << 23) ))
int1=$(( \
old_int0 \
^ old_int1 \
^ (old_int0 >> 17) \
^ (old_int1 >> 26) \
))
rnd=$(( int1 + old_int1 ))
if (( full_range )); then
if (( rnd >= 0 )); then
printf -- '%u\n' "$rnd"
(( int_count-- ))
fi
continue
fi
if (( rnd >= 0 && rnd >= threshold )); then
printf -- '%u\n' "$(( (rnd % rand_range) + int_min ))"
(( int_count-- ))
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment