mirror of
https://github.com/boostorg/random.git
synced 2026-01-19 04:22:17 +00:00
Add xoshiro128f
This commit is contained in:
@@ -410,6 +410,77 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This is xoshiro128+ 1.0, our best and fastest 32-bit generator for 32-bit
|
||||
* floating-point numbers. We suggest to use its upper bits for
|
||||
* floating-point generation, as it is slightly faster than xoshiro128**.
|
||||
* It passes all tests we are aware of except for
|
||||
* linearity tests, as the lowest four bits have low linear complexity, so
|
||||
* if low linear complexity is not considered an issue (as it is usually
|
||||
* the case) it can be used to generate 32-bit outputs, too.
|
||||
*
|
||||
* We suggest to use a sign test to extract a random Boolean value, and
|
||||
* right shifts to extract subsets of bits.
|
||||
*
|
||||
* The state must be seeded so that it is not everywhere zero.
|
||||
*/
|
||||
|
||||
class xoshiro128f final : public detail::xoshiro_base<xoshiro128f, 4, float, std::uint32_t>
|
||||
{
|
||||
private:
|
||||
|
||||
using Base = detail::xoshiro_base<xoshiro128f, 4, float, std::uint32_t>;
|
||||
|
||||
public:
|
||||
|
||||
using Base::Base;
|
||||
|
||||
inline std::uint32_t next_int() noexcept
|
||||
{
|
||||
const std::uint32_t result = state_[0] + state_[3];
|
||||
|
||||
const std::uint32_t t = state_[1] << 9;
|
||||
|
||||
state_[2] ^= state_[0];
|
||||
state_[3] ^= state_[1];
|
||||
state_[1] ^= state_[2];
|
||||
state_[0] ^= state_[3];
|
||||
|
||||
state_[2] ^= t;
|
||||
|
||||
state_[3] = boost::core::rotl(state_[3], 11);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline result_type next() noexcept
|
||||
{
|
||||
#if (__cplusplus >= 201703L || _MSVC_LANG >= 201703L) && defined(__cpp_hex_float) && __cpp_hex_float >= 201603L
|
||||
return static_cast<float>((next_int() >> 8)) * 0x1.0p-24f;
|
||||
#else
|
||||
return static_cast<float>((next_int() >> 8)) * 5.9604645e-08f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static constexpr result_type (min)() noexcept
|
||||
{
|
||||
#if (__cplusplus >= 201703L || _MSVC_LANG >= 201703L) && defined(__cpp_hex_float) && __cpp_hex_float >= 201603L
|
||||
return static_cast<float>((std::numeric_limits<std::uint32_t>::min)() >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
return static_cast<float>((std::numeric_limits<std::uint64_t>::min)() >> 8) * 5.9604645e-08f;
|
||||
#endif
|
||||
}
|
||||
|
||||
static constexpr result_type (max)() noexcept
|
||||
{
|
||||
#if (__cplusplus >= 201703L || _MSVC_LANG >= 201703L) && defined(__cpp_hex_float) && __cpp_hex_float >= 201603L
|
||||
return static_cast<float>((std::numeric_limits<std::uint32_t>::max)() >> 8) * 0x1.0p-24f;
|
||||
#else
|
||||
return static_cast<float>((std::numeric_limits<std::uint64_t>::max)() >> 8) * 5.9604645e-08f;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace random
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ run test_xoshiro128pp.cpp /boost/test//boost_unit_test_framework ;
|
||||
run test_comp_xoshiro128pp.cpp ;
|
||||
run test_xoshiro128mm.cpp /boost/test//boost_unit_test_framework ;
|
||||
run test_comp_xoshiro128mm.cpp ;
|
||||
run test_xoshiro128f.cpp /boost/test//boost_unit_test_framework ;
|
||||
run test_comp_xoshiro128f.cpp ;
|
||||
|
||||
run niederreiter_base2_validate.cpp /boost/test//boost_unit_test_framework ;
|
||||
run sobol_validate.cpp /boost/test//boost_unit_test_framework ;
|
||||
|
||||
291
test/test_comp_xoshiro128f.cpp
Normal file
291
test/test_comp_xoshiro128f.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright Matt Borland 2025.
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* This file copies and pastes the original code for comparison under the following license
|
||||
*
|
||||
* Written in 2019 by David Blackman and Sebastiano Vigna (vigna@acm.org)
|
||||
*
|
||||
* To the extent possible under law, the author has dedicated all copyright
|
||||
* and related and neighboring rights to this software to the public domain
|
||||
* worldwide.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <boost/random/xoshiro.hpp>
|
||||
#include <boost/random/splitmix64.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
using std::uint32_t;
|
||||
|
||||
static inline uint32_t rotl(const uint32_t x, int k) {
|
||||
return (x << k) | (x >> (32 - k));
|
||||
}
|
||||
|
||||
|
||||
static uint32_t s[4];
|
||||
|
||||
uint32_t next(void) {
|
||||
const uint32_t result = s[0] + s[3];
|
||||
|
||||
const uint32_t t = s[1] << 9;
|
||||
|
||||
s[2] ^= s[0];
|
||||
s[3] ^= s[1];
|
||||
s[1] ^= s[2];
|
||||
s[0] ^= s[3];
|
||||
|
||||
s[2] ^= t;
|
||||
|
||||
s[3] = rotl(s[3], 11);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* This is the jump function for the generator. It is equivalent
|
||||
to 2^64 calls to next(); it can be used to generate 2^64
|
||||
non-overlapping subsequences for parallel computations. */
|
||||
|
||||
void jump(void) {
|
||||
static const uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b };
|
||||
|
||||
uint32_t s0 = 0;
|
||||
uint32_t s1 = 0;
|
||||
uint32_t s2 = 0;
|
||||
uint32_t s3 = 0;
|
||||
for(int i = 0; i < sizeof JUMP / sizeof *JUMP; i++)
|
||||
for(int b = 0; b < 32; b++) {
|
||||
if (JUMP[i] & UINT32_C(1) << b) {
|
||||
s0 ^= s[0];
|
||||
s1 ^= s[1];
|
||||
s2 ^= s[2];
|
||||
s3 ^= s[3];
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
s[0] = s0;
|
||||
s[1] = s1;
|
||||
s[2] = s2;
|
||||
s[3] = s3;
|
||||
}
|
||||
|
||||
|
||||
/* This is the long-jump function for the generator. It is equivalent to
|
||||
2^96 calls to next(); it can be used to generate 2^32 starting points,
|
||||
from each of which jump() will generate 2^32 non-overlapping
|
||||
subsequences for parallel distributed computations. */
|
||||
|
||||
void long_jump(void) {
|
||||
static const uint32_t LONG_JUMP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 };
|
||||
|
||||
uint32_t s0 = 0;
|
||||
uint32_t s1 = 0;
|
||||
uint32_t s2 = 0;
|
||||
uint32_t s3 = 0;
|
||||
for(int i = 0; i < sizeof LONG_JUMP / sizeof *LONG_JUMP; i++)
|
||||
for(int b = 0; b < 32; b++) {
|
||||
if (LONG_JUMP[i] & UINT32_C(1) << b) {
|
||||
s0 ^= s[0];
|
||||
s1 ^= s[1];
|
||||
s2 ^= s[2];
|
||||
s3 ^= s[3];
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
s[0] = s0;
|
||||
s[1] = s1;
|
||||
s[2] = s2;
|
||||
s[3] = s3;
|
||||
}
|
||||
|
||||
void test_no_seed()
|
||||
{
|
||||
// Default initialized to contain splitmix64 values
|
||||
boost::random::xoshiro128f boost_rng;
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
boost_rng();
|
||||
}
|
||||
|
||||
boost::random::splitmix64 gen;
|
||||
for (auto& i : s)
|
||||
{
|
||||
i = gen();
|
||||
}
|
||||
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
const auto final_state = boost_rng.state();
|
||||
|
||||
for (std::size_t i {}; i < final_state.size(); ++i)
|
||||
{
|
||||
BOOST_TEST_EQ(final_state[i], s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_basic_seed()
|
||||
{
|
||||
// Default initialized to contain splitmix64 values
|
||||
boost::random::xoshiro128f boost_rng(UINT32_C(42));
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
boost_rng();
|
||||
}
|
||||
|
||||
boost::random::splitmix64 gen(42ULL);
|
||||
for (auto& i : s)
|
||||
{
|
||||
i = gen();
|
||||
}
|
||||
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
const auto final_state = boost_rng.state();
|
||||
|
||||
for (std::size_t i {}; i < final_state.size(); ++i)
|
||||
{
|
||||
BOOST_TEST_EQ(final_state[i], s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_jump()
|
||||
{
|
||||
// Default initialized to contain splitmix64 values
|
||||
boost::random::xoshiro128f boost_rng;
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
boost_rng();
|
||||
}
|
||||
|
||||
boost::random::splitmix64 gen;
|
||||
for (auto& i : s)
|
||||
{
|
||||
i = gen();
|
||||
}
|
||||
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
boost_rng.jump();
|
||||
jump();
|
||||
|
||||
const auto final_state = boost_rng.state();
|
||||
|
||||
for (std::size_t i {}; i < final_state.size(); ++i)
|
||||
{
|
||||
BOOST_TEST_EQ(final_state[i], s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_long_jump()
|
||||
{
|
||||
// Default initialized to contain splitmix64 values
|
||||
boost::random::xoshiro128f boost_rng;
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
boost_rng();
|
||||
}
|
||||
|
||||
boost::random::splitmix64 gen;
|
||||
for (auto& i : s)
|
||||
{
|
||||
i = gen();
|
||||
}
|
||||
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
boost_rng.long_jump();
|
||||
long_jump();
|
||||
|
||||
const auto final_state = boost_rng.state();
|
||||
|
||||
for (std::size_t i {}; i < final_state.size(); ++i)
|
||||
{
|
||||
BOOST_TEST_EQ(final_state[i], s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(_MSVC_LANG) || _MSVC_LANG >= 202002L
|
||||
|
||||
static inline float to_float(uint32_t x) {
|
||||
const union { uint32_t i; float f; } u = { .i = (0x7F << 23) | (x >> 9) };
|
||||
return u.f - 1.0f;
|
||||
}
|
||||
|
||||
void test_float()
|
||||
{
|
||||
// Default initialized to contain splitmix64 values
|
||||
boost::random::xoshiro128f boost_rng;
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
boost_rng();
|
||||
}
|
||||
|
||||
boost::random::splitmix64 gen;
|
||||
for (auto& i : s)
|
||||
{
|
||||
i = gen();
|
||||
}
|
||||
|
||||
for (int i {}; i < 10000; ++i)
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
const auto final_state = boost_rng.state();
|
||||
|
||||
for (std::size_t i {}; i < final_state.size(); ++i)
|
||||
{
|
||||
BOOST_TEST_EQ(final_state[i], s[i]);
|
||||
}
|
||||
|
||||
const auto boost_double = boost_rng();
|
||||
const auto ref_double = to_float(next());
|
||||
|
||||
BOOST_TEST(std::fabs(boost_double - ref_double) < std::numeric_limits<float>::epsilon());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
test_no_seed();
|
||||
test_basic_seed();
|
||||
test_jump();
|
||||
test_long_jump();
|
||||
|
||||
#if !defined(_MSVC_LANG) || _MSVC_LANG >= 202002L
|
||||
test_float();
|
||||
#endif
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
24
test/test_xoshiro128f.cpp
Normal file
24
test/test_xoshiro128f.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/* test_xoshiro256d.cpp
|
||||
*
|
||||
* Copyright Steven Watanabe 2011
|
||||
* Copyright Matt Borland 2022
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE_1_0.txt or copy at
|
||||
* http://www.boost.org/LICENSE_1_0.txt)
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <boost/random/xoshiro.hpp>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
#define BOOST_RANDOM_URNG boost::random::xoshiro128f
|
||||
#define BOOST_RANDOM_CPP11_URNG
|
||||
|
||||
// principal operation validated with CLHEP, values by experiment
|
||||
#define BOOST_RANDOM_VALIDATION_VALUE 0.496670783f
|
||||
#define BOOST_RANDOM_SEED_SEQ_VALIDATION_VALUE 0.380300581f
|
||||
|
||||
#include "test_generator.ipp"
|
||||
Reference in New Issue
Block a user