#ifndef LLVM_LIBC_BENCHMARKS_GPU_RANDOM_H
#define LLVM_LIBC_BENCHMARKS_GPU_RANDOM_H
#include "hdr/stdint_proxy.h"
#include "src/__support/CPP/algorithm.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/types.h"
#include "src/__support/sign.h"
namespace LIBC_NAMESPACE_DECL {
namespace benchmarks {
class RandomGenerator {
uint64_t state;
static LIBC_INLINE uint64_t splitmix64(uint64_t x) noexcept {
x += 0x9E3779B97F4A7C15ULL;
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
x = (x ^ (x >> 31));
return x ? x : 0x9E3779B97F4A7C15ULL;
}
public:
explicit LIBC_INLINE RandomGenerator(uint64_t seed) noexcept
: state(splitmix64(seed)) {}
LIBC_INLINE uint64_t next64() noexcept {
uint64_t x = state;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
state = x;
return x * 0x2545F4914F6CDD1DULL;
}
LIBC_INLINE uint32_t next32() noexcept {
return static_cast<uint32_t>(next64() >> 32);
}
LIBC_INLINE uint16_t next16() noexcept {
return static_cast<uint16_t>(next64() >> 48);
}
};
template <typename T> class UniformExponent {
static_assert(cpp::is_same_v<T, float16> || cpp::is_same_v<T, float> ||
cpp::is_same_v<T, double>,
"UniformExponent supports float16, float, and double");
using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
using Storage = typename FPBits::StorageType;
public:
explicit UniformExponent(int min_exp = -FPBits::EXP_BIAS,
int max_exp = FPBits::EXP_BIAS,
cpp::optional<Sign> forced_sign = cpp::nullopt)
: min_exp(clamp_exponent(cpp::min(min_exp, max_exp))),
max_exp(clamp_exponent(cpp::max(min_exp, max_exp))),
forced_sign(forced_sign) {}
LIBC_INLINE T operator()(RandomGenerator &rng) const noexcept {
auto sample_in_range = [&](uint64_t r) -> int32_t {
const uint64_t range = static_cast<uint64_t>(
static_cast<int64_t>(max_exp) - static_cast<int64_t>(min_exp) + 1);
const uint64_t threshold = (-range) % range;
while (r < threshold)
r = rng.next64();
return static_cast<int32_t>(min_exp + static_cast<int64_t>(r % range));
};
const int32_t e = sample_in_range(rng.next64());
FPBits xbits([&] {
if constexpr (cpp::is_same_v<T, double>)
return FPBits(rng.next64());
else if constexpr (cpp::is_same_v<T, float>)
return FPBits(rng.next32());
else
return FPBits(rng.next16());
}());
if (e == -FPBits::EXP_BIAS) {
xbits.set_biased_exponent(Storage(0));
if (xbits.get_mantissa() == Storage(0))
xbits.set_mantissa(Storage(1));
} else {
const int32_t biased = e + FPBits::EXP_BIAS;
xbits.set_biased_exponent(static_cast<Storage>(biased));
}
if (forced_sign)
xbits.set_sign(*forced_sign);
return xbits.get_val();
}
private:
static LIBC_INLINE int clamp_exponent(int val) noexcept {
if (val < -FPBits::EXP_BIAS)
return -FPBits::EXP_BIAS;
if (val > FPBits::EXP_BIAS)
return FPBits::EXP_BIAS;
return val;
}
const int min_exp;
const int max_exp;
const cpp::optional<Sign> forced_sign;
};
template <typename T> class UniformLinear {
static_assert(cpp::is_same_v<T, float16> || cpp::is_same_v<T, float> ||
cpp::is_same_v<T, double>,
"UniformLinear supports float16, float, and double");
using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
using Storage = typename FPBits::StorageType;
static constexpr T MAX_NORMAL = FPBits::max_normal().get_val();
public:
explicit UniformLinear(T min_val = -MAX_NORMAL, T max_val = MAX_NORMAL)
: min_val(clamp_val(cpp::min(min_val, max_val))),
max_val(clamp_val(cpp::max(min_val, max_val))) {}
LIBC_INLINE T operator()(RandomGenerator &rng) const noexcept {
double u = standard_uniform(rng.next64());
double a = static_cast<double>(min_val);
double b = static_cast<double>(max_val);
double y = a + (b - a) * u;
return static_cast<T>(y);
}
private:
static LIBC_INLINE T clamp_val(T val) noexcept {
if (val < -MAX_NORMAL)
return -MAX_NORMAL;
if (val > MAX_NORMAL)
return MAX_NORMAL;
return val;
}
static LIBC_INLINE double standard_uniform(uint64_t x) noexcept {
constexpr int PREC_BITS =
LIBC_NAMESPACE::fputil::FPBits<double>::SIG_LEN + 1;
constexpr int SHIFT_BITS = LIBC_NAMESPACE::fputil::FPBits<double>::EXP_LEN;
constexpr double INV = 1.0 / static_cast<double>(1ULL << PREC_BITS);
return static_cast<double>(x >> SHIFT_BITS) * INV;
}
const T min_val;
const T max_val;
};
}
}
#endif