-- e54b9c7bbb0c58475676c268e2e19c69f4bce48a by Jorg Brown <jorg@google.com>: Tweak ABSL_PREDICT_TRUE slightly, for better code on some platforms and/or optimization levels. "false || (x)" is more verbose than "!!(x)", but ultimately more efficient. For example, given this code: void InitIfNecessary() { if (ABSL_PREDICT_TRUE(NeedsInit())) { SlowInitIfNecessary(); } } Clang with default optimization level will produce: Before this CL After this CL InitIfNecessary: InitIfNecessary: push rbp push rbp mov rbp, rsp mov rbp, rsp call NeedsInit call NeedsInit xor al, -1 xor al, -1 test al, 1 test al, 1 jne .LBB2_1 jne .LBB3_1 jmp .LBB2_2 jmp .LBB3_2 .LBB2_1: .LBB3_1: call SlowInitIfNecessary call SlowInitIfNecessary .LBB2_2: .LBB3_2: pop rbp pop rbp ret ret PiperOrigin-RevId: 276401386 -- 0a3c4dfd8342bf2b1b11a87f1c662c883f73cab7 by Abseil Team <absl-team@google.com>: Fix comment nit: sem_open => sem_init. The code calls sem_init, not sem_open, to initialize an unnamed semaphore. (sem_open creates or opens a named semaphore.) PiperOrigin-RevId: 276344072 -- b36a664e9459057509a90e83d3482e1d3a4c44c7 by Abseil Team <absl-team@google.com>: Fix typo in flat_hash_map.h: exchaged -> exchanged PiperOrigin-RevId: 276295792 -- 7bbd8d18276eb110c8335743e35fceb662ddf3d6 by Samuel Benzaquen <sbenza@google.com>: Add assertions to verify use of iterators. PiperOrigin-RevId: 276283300 -- 677398a8ffcb1f59182cffe57a4fe7ff147a0404 by Laramie Leavitt <lar@google.com>: Migrate distribution_impl.h/cc to generate_real.h/cc. Combine the methods RandU64To<Float,Double> into a single method: GenerateRealFromBits(). Remove rejection sampling from absl::uniform_real_distribution. PiperOrigin-RevId: 276158675 -- c60c9d11d24b0c546329d998e78e15a84b3153f5 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 276126962 -- 4c840cab6a8d86efa29b397cafaf7520eece68cc by Andy Soffer <asoffer@google.com>: Update CMakeLists.txt to address https://github.com/abseil/abseil-cpp/issues/365. This does not cover every platform, but it does at least address the first-order issue of assuming gcc implies x86. PiperOrigin-RevId: 276116253 -- 98da366e6b5d51afe5d7ac6722126aca23d85ee6 by Abseil Team <absl-team@google.com>: Internal change PiperOrigin-RevId: 276097452 GitOrigin-RevId: e54b9c7bbb0c58475676c268e2e19c69f4bce48a Change-Id: I02d84454bb71ab21ad3d39650acf6cc6e36f58d7pull/397/head
parent
19b021cb3f
commit
078b89b3c0
28 changed files with 739 additions and 370 deletions
@ -0,0 +1,84 @@ |
||||
// Copyright 2019 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/base/internal/exponential_biased.h" |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#include <atomic> |
||||
#include <cmath> |
||||
#include <limits> |
||||
|
||||
#include "absl/base/attributes.h" |
||||
#include "absl/base/optimization.h" |
||||
|
||||
namespace absl { |
||||
namespace base_internal { |
||||
|
||||
// The algorithm generates a random number between 0 and 1 and applies the
|
||||
// inverse cumulative distribution function for an exponential. Specifically:
|
||||
// Let m be the inverse of the sample period, then the probability
|
||||
// distribution function is m*exp(-mx) so the CDF is
|
||||
// p = 1 - exp(-mx), so
|
||||
// q = 1 - p = exp(-mx)
|
||||
// log_e(q) = -mx
|
||||
// -log_e(q)/m = x
|
||||
// log_2(q) * (-log_e(2) * 1/m) = x
|
||||
// In the code, q is actually in the range 1 to 2**26, hence the -26 below
|
||||
int64_t ExponentialBiased::Get(int64_t mean) { |
||||
if (ABSL_PREDICT_FALSE(!initialized_)) { |
||||
Initialize(); |
||||
} |
||||
|
||||
uint64_t rng = NextRandom(rng_); |
||||
rng_ = rng; |
||||
|
||||
// Take the top 26 bits as the random number
|
||||
// (This plus the 1<<58 sampling bound give a max possible step of
|
||||
// 5194297183973780480 bytes.)
|
||||
// The uint32_t cast is to prevent a (hard-to-reproduce) NAN
|
||||
// under piii debug for some binaries.
|
||||
double q = static_cast<uint32_t>(rng >> (kPrngNumBits - 26)) + 1.0; |
||||
// Put the computed p-value through the CDF of a geometric.
|
||||
double interval = (std::log2(q) - 26) * (-std::log(2.0) * mean); |
||||
// Very large values of interval overflow int64_t. To avoid that, we will cheat
|
||||
// and clamp any huge values to (int64_t max)/2. This is a potential source of
|
||||
// bias, but the mean would need to be such a large value that it's not likely
|
||||
// to come up. For example, with a mean of 1e18, the probability of hitting
|
||||
// this condition is about 1/1000. For a mean of 1e17, standard calculators
|
||||
// claim that this event won't happen.
|
||||
if (interval > static_cast<double>(std::numeric_limits<int64_t>::max() / 2)) { |
||||
return std::numeric_limits<int64_t>::max() / 2; |
||||
} |
||||
|
||||
return static_cast<int64_t>(interval); |
||||
} |
||||
|
||||
void ExponentialBiased::Initialize() { |
||||
// We don't get well distributed numbers from `this` so we call NextRandom() a
|
||||
// bunch to mush the bits around. We use a global_rand to handle the case
|
||||
// where the same thread (by memory address) gets created and destroyed
|
||||
// repeatedly.
|
||||
ABSL_CONST_INIT static std::atomic<uint32_t> global_rand(0); |
||||
uint64_t r = reinterpret_cast<uint64_t>(this) + |
||||
global_rand.fetch_add(1, std::memory_order_relaxed); |
||||
for (int i = 0; i < 20; ++i) { |
||||
r = NextRandom(r); |
||||
} |
||||
rng_ = r; |
||||
initialized_ = true; |
||||
} |
||||
|
||||
} // namespace base_internal
|
||||
} // namespace absl
|
@ -0,0 +1,77 @@ |
||||
// Copyright 2019 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ |
||||
#define ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_ |
||||
|
||||
#include <stdint.h> |
||||
|
||||
namespace absl { |
||||
namespace base_internal { |
||||
|
||||
// ExponentialBiased provides a small and fast random number generator for a
|
||||
// rounded exponential distribution. This generator doesn't requires very little
|
||||
// state doesn't impose synchronization overhead, which makes it useful in some
|
||||
// specialized scenarios.
|
||||
//
|
||||
// For the generated variable X, X ~ floor(Exponential(1/mean)). The floor
|
||||
// operation introduces a small amount of bias, but the distribution is useful
|
||||
// to generate a wait time. That is, if an operation is supposed to happen on
|
||||
// average to 1/mean events, then the generated variable X will describe how
|
||||
// many events to skip before performing the operation and computing a new X.
|
||||
//
|
||||
// The mathematically precise distribution to use for integer wait times is a
|
||||
// Geometric distribution, but a Geometric distribution takes slightly more time
|
||||
// to compute and when the mean is large (say, 100+), the Geometric distribution
|
||||
// is hard to distinguish from the result of ExponentialBiased.
|
||||
//
|
||||
// This class is thread-compatible.
|
||||
class ExponentialBiased { |
||||
public: |
||||
// The number of bits set by NextRandom.
|
||||
static constexpr int kPrngNumBits = 48; |
||||
|
||||
// Generates the floor of an exponentially distributed random variable by
|
||||
// rounding the value down to the nearest integer. The result will be in the
|
||||
// range [0, int64_t max / 2].
|
||||
int64_t Get(int64_t mean); |
||||
|
||||
// Computes a random number in the range [0, 1<<(kPrngNumBits+1) - 1]
|
||||
//
|
||||
// This is public to enable testing.
|
||||
static uint64_t NextRandom(uint64_t rnd); |
||||
|
||||
private: |
||||
void Initialize(); |
||||
|
||||
uint64_t rng_{0}; |
||||
bool initialized_{false}; |
||||
}; |
||||
|
||||
// Returns the next prng value.
|
||||
// pRNG is: aX+b mod c with a = 0x5DEECE66D, b = 0xB, c = 1<<48
|
||||
// This is the lrand64 generator.
|
||||
inline uint64_t ExponentialBiased::NextRandom(uint64_t rnd) { |
||||
const uint64_t prng_mult = uint64_t{0x5DEECE66D}; |
||||
const uint64_t prng_add = 0xB; |
||||
const uint64_t prng_mod_power = 48; |
||||
const uint64_t prng_mod_mask = |
||||
~((~static_cast<uint64_t>(0)) << prng_mod_power); |
||||
return (prng_mult * rnd + prng_add) & prng_mod_mask; |
||||
} |
||||
|
||||
} // namespace base_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_BASE_INTERNAL_EXPONENTIAL_BIASED_H_
|
@ -0,0 +1,168 @@ |
||||
// Copyright 2019 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/base/internal/exponential_biased.h" |
||||
|
||||
#include <stddef.h> |
||||
|
||||
#include <cmath> |
||||
#include <cstdint> |
||||
#include <vector> |
||||
|
||||
#include "gmock/gmock.h" |
||||
#include "gtest/gtest.h" |
||||
#include "absl/strings/str_cat.h" |
||||
|
||||
using ::testing::Ge; |
||||
|
||||
namespace absl { |
||||
namespace base_internal { |
||||
|
||||
MATCHER_P2(IsBetween, a, b, |
||||
absl::StrCat(std::string(negation ? "isn't" : "is"), " between ", a, |
||||
" and ", b)) { |
||||
return a <= arg && arg <= b; |
||||
} |
||||
|
||||
// Tests of the quality of the random numbers generated
|
||||
// This uses the Anderson Darling test for uniformity.
|
||||
// See "Evaluating the Anderson-Darling Distribution" by Marsaglia
|
||||
// for details.
|
||||
|
||||
// Short cut version of ADinf(z), z>0 (from Marsaglia)
|
||||
// This returns the p-value for Anderson Darling statistic in
|
||||
// the limit as n-> infinity. For finite n, apply the error fix below.
|
||||
double AndersonDarlingInf(double z) { |
||||
if (z < 2) { |
||||
return exp(-1.2337141 / z) / sqrt(z) * |
||||
(2.00012 + |
||||
(0.247105 - |
||||
(0.0649821 - (0.0347962 - (0.011672 - 0.00168691 * z) * z) * z) * |
||||
z) * |
||||
z); |
||||
} |
||||
return exp( |
||||
-exp(1.0776 - |
||||
(2.30695 - |
||||
(0.43424 - (0.082433 - (0.008056 - 0.0003146 * z) * z) * z) * z) * |
||||
z)); |
||||
} |
||||
|
||||
// Corrects the approximation error in AndersonDarlingInf for small values of n
|
||||
// Add this to AndersonDarlingInf to get a better approximation
|
||||
// (from Marsaglia)
|
||||
double AndersonDarlingErrFix(int n, double x) { |
||||
if (x > 0.8) { |
||||
return (-130.2137 + |
||||
(745.2337 - |
||||
(1705.091 - (1950.646 - (1116.360 - 255.7844 * x) * x) * x) * x) * |
||||
x) / |
||||
n; |
||||
} |
||||
double cutoff = 0.01265 + 0.1757 / n; |
||||
if (x < cutoff) { |
||||
double t = x / cutoff; |
||||
t = sqrt(t) * (1 - t) * (49 * t - 102); |
||||
return t * (0.0037 / (n * n) + 0.00078 / n + 0.00006) / n; |
||||
} else { |
||||
double t = (x - cutoff) / (0.8 - cutoff); |
||||
t = -0.00022633 + |
||||
(6.54034 - (14.6538 - (14.458 - (8.259 - 1.91864 * t) * t) * t) * t) * |
||||
t; |
||||
return t * (0.04213 + 0.01365 / n) / n; |
||||
} |
||||
} |
||||
|
||||
// Returns the AndersonDarling p-value given n and the value of the statistic
|
||||
double AndersonDarlingPValue(int n, double z) { |
||||
double ad = AndersonDarlingInf(z); |
||||
double errfix = AndersonDarlingErrFix(n, ad); |
||||
return ad + errfix; |
||||
} |
||||
|
||||
double AndersonDarlingStatistic(const std::vector<double>& random_sample) { |
||||
int n = random_sample.size(); |
||||
double ad_sum = 0; |
||||
for (int i = 0; i < n; i++) { |
||||
ad_sum += (2 * i + 1) * |
||||
std::log(random_sample[i] * (1 - random_sample[n - 1 - i])); |
||||
} |
||||
double ad_statistic = -n - 1 / static_cast<double>(n) * ad_sum; |
||||
return ad_statistic; |
||||
} |
||||
|
||||
// Tests if the array of doubles is uniformly distributed.
|
||||
// Returns the p-value of the Anderson Darling Statistic
|
||||
// for the given set of sorted random doubles
|
||||
// See "Evaluating the Anderson-Darling Distribution" by
|
||||
// Marsaglia and Marsaglia for details.
|
||||
double AndersonDarlingTest(const std::vector<double>& random_sample) { |
||||
double ad_statistic = AndersonDarlingStatistic(random_sample); |
||||
double p = AndersonDarlingPValue(random_sample.size(), ad_statistic); |
||||
return p; |
||||
} |
||||
|
||||
// Testing that NextRandom generates uniform random numbers. Applies the
|
||||
// Anderson-Darling test for uniformity
|
||||
TEST(ExponentialBiasedTest, TestNextRandom) { |
||||
for (auto n : std::vector<int>({ |
||||
10, // Check short-range correlation
|
||||
100, 1000, |
||||
10000 // Make sure there's no systemic error
|
||||
})) { |
||||
uint64_t x = 1; |
||||
// This assumes that the prng returns 48 bit numbers
|
||||
uint64_t max_prng_value = static_cast<uint64_t>(1) << 48; |
||||
// Initialize.
|
||||
for (int i = 1; i <= 20; i++) { |
||||
x = ExponentialBiased::NextRandom(x); |
||||
} |
||||
std::vector<uint64_t> int_random_sample(n); |
||||
// Collect samples
|
||||
for (int i = 0; i < n; i++) { |
||||
int_random_sample[i] = x; |
||||
x = ExponentialBiased::NextRandom(x); |
||||
} |
||||
// First sort them...
|
||||
std::sort(int_random_sample.begin(), int_random_sample.end()); |
||||
std::vector<double> random_sample(n); |
||||
// Convert them to uniform randoms (in the range [0,1])
|
||||
for (int i = 0; i < n; i++) { |
||||
random_sample[i] = |
||||
static_cast<double>(int_random_sample[i]) / max_prng_value; |
||||
} |
||||
// Now compute the Anderson-Darling statistic
|
||||
double ad_pvalue = AndersonDarlingTest(random_sample); |
||||
EXPECT_GT(std::min(ad_pvalue, 1 - ad_pvalue), 0.0001) |
||||
<< "prng is not uniform: n = " << n << " p = " << ad_pvalue; |
||||
} |
||||
} |
||||
|
||||
// The generator needs to be available as a thread_local and as a static
|
||||
// variable.
|
||||
TEST(ExponentialBiasedTest, InitializationModes) { |
||||
ABSL_CONST_INIT static ExponentialBiased eb_static; |
||||
EXPECT_THAT(eb_static.Get(2), Ge(0)); |
||||
|
||||
#if ABSL_HAVE_THREAD_LOCAL |
||||
thread_local ExponentialBiased eb_thread; |
||||
EXPECT_THAT(eb_thread.Get(2), Ge(0)); |
||||
#endif |
||||
|
||||
ExponentialBiased eb_stack; |
||||
EXPECT_THAT(eb_stack.Get(2), Ge(0)); |
||||
} |
||||
|
||||
} // namespace base_internal
|
||||
} // namespace absl
|
@ -1,194 +0,0 @@ |
||||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ |
||||
#define ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_ |
||||
|
||||
// This file contains some implementation details which are used by one or more
|
||||
// of the absl random number distributions.
|
||||
|
||||
#include <cfloat> |
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <cstring> |
||||
#include <limits> |
||||
#include <type_traits> |
||||
|
||||
#if (defined(_WIN32) || defined(_WIN64)) && defined(_M_IA64) |
||||
#include <intrin.h> // NOLINT(build/include_order) |
||||
#pragma intrinsic(_umul128) |
||||
#define ABSL_INTERNAL_USE_UMUL128 1 |
||||
#endif |
||||
|
||||
#include "absl/base/config.h" |
||||
#include "absl/base/internal/bits.h" |
||||
#include "absl/numeric/int128.h" |
||||
#include "absl/random/internal/fastmath.h" |
||||
#include "absl/random/internal/traits.h" |
||||
|
||||
namespace absl { |
||||
namespace random_internal { |
||||
|
||||
// Creates a double from `bits`, with the template fields controlling the
|
||||
// output.
|
||||
//
|
||||
// RandU64To is both more efficient and generates more unique values in the
|
||||
// result interval than known implementations of std::generate_canonical().
|
||||
//
|
||||
// The `Signed` parameter controls whether positive, negative, or both are
|
||||
// returned (thus affecting the output interval).
|
||||
// When Signed == SignedValueT, range is U(-1, 1)
|
||||
// When Signed == NegativeValueT, range is U(-1, 0)
|
||||
// When Signed == PositiveValueT, range is U(0, 1)
|
||||
//
|
||||
// When the `IncludeZero` parameter is true, the function may return 0 for some
|
||||
// inputs, otherwise it never returns 0.
|
||||
//
|
||||
// The `ExponentBias` parameter determines the scale of the output range by
|
||||
// adjusting the exponent.
|
||||
//
|
||||
// When a value in U(0,1) is required, use:
|
||||
// RandU64ToDouble<PositiveValueT, true, 0>();
|
||||
//
|
||||
// When a value in U(-1,1) is required, use:
|
||||
// RandU64ToDouble<SignedValueT, false, 0>() => U(-1, 1)
|
||||
// This generates more distinct values than the mathematically equivalent
|
||||
// expression `U(0, 1) * 2.0 - 1.0`, and is preferable.
|
||||
//
|
||||
// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
|
||||
// RandU64ToDouble<PositiveValueT, false, 1>(); => U(0, 2)
|
||||
// RandU64ToDouble<PositiveValueT, false, -1>(); => U(0, 0.5)
|
||||
//
|
||||
|
||||
// Tristate types controlling the output.
|
||||
struct PositiveValueT {}; |
||||
struct NegativeValueT {}; |
||||
struct SignedValueT {}; |
||||
|
||||
// RandU64ToDouble is the double-result variant of RandU64To, described above.
|
||||
template <typename Signed, bool IncludeZero, int ExponentBias = 0> |
||||
inline double RandU64ToDouble(uint64_t bits) { |
||||
static_assert(std::is_same<Signed, PositiveValueT>::value || |
||||
std::is_same<Signed, NegativeValueT>::value || |
||||
std::is_same<Signed, SignedValueT>::value, |
||||
""); |
||||
|
||||
// Maybe use the left-most bit for a sign bit.
|
||||
uint64_t sign = std::is_same<Signed, NegativeValueT>::value |
||||
? 0x8000000000000000ull |
||||
: 0; // Sign bits.
|
||||
|
||||
if (std::is_same<Signed, SignedValueT>::value) { |
||||
sign = bits & 0x8000000000000000ull; |
||||
bits = bits & 0x7FFFFFFFFFFFFFFFull; |
||||
} |
||||
if (IncludeZero) { |
||||
if (bits == 0u) return 0; |
||||
} |
||||
|
||||
// Number of leading zeros is mapped to the exponent: 2^-clz
|
||||
int clz = base_internal::CountLeadingZeros64(bits); |
||||
// Shift number left to erase leading zeros.
|
||||
bits <<= IncludeZero ? clz : (clz & 63); |
||||
|
||||
// Shift number right to remove bits that overflow double mantissa. The
|
||||
// direction of the shift depends on `clz`.
|
||||
bits >>= (64 - DBL_MANT_DIG); |
||||
|
||||
// Compute IEEE 754 double exponent.
|
||||
// In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the
|
||||
// exponent to account for that.
|
||||
const uint64_t exp = |
||||
(std::is_same<Signed, SignedValueT>::value ? 1023U : 1022U) + |
||||
static_cast<uint64_t>(ExponentBias - clz); |
||||
constexpr int kExp = DBL_MANT_DIG - 1; |
||||
// Construct IEEE 754 double from exponent and mantissa.
|
||||
const uint64_t val = sign | (exp << kExp) | (bits & ((1ULL << kExp) - 1U)); |
||||
|
||||
double res; |
||||
static_assert(sizeof(res) == sizeof(val), "double is not 64 bit"); |
||||
// Memcpy value from "val" to "res" to avoid aliasing problems. Assumes that
|
||||
// endian-ness is same for double and uint64_t.
|
||||
std::memcpy(&res, &val, sizeof(res)); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
// RandU64ToFloat is the float-result variant of RandU64To, described above.
|
||||
template <typename Signed, bool IncludeZero, int ExponentBias = 0> |
||||
inline float RandU64ToFloat(uint64_t bits) { |
||||
static_assert(std::is_same<Signed, PositiveValueT>::value || |
||||
std::is_same<Signed, NegativeValueT>::value || |
||||
std::is_same<Signed, SignedValueT>::value, |
||||
""); |
||||
|
||||
// Maybe use the left-most bit for a sign bit.
|
||||
uint64_t sign = std::is_same<Signed, NegativeValueT>::value |
||||
? 0x80000000ul |
||||
: 0; // Sign bits.
|
||||
|
||||
if (std::is_same<Signed, SignedValueT>::value) { |
||||
uint64_t a = bits & 0x8000000000000000ull; |
||||
sign = static_cast<uint32_t>(a >> 32); |
||||
bits = bits & 0x7FFFFFFFFFFFFFFFull; |
||||
} |
||||
if (IncludeZero) { |
||||
if (bits == 0u) return 0; |
||||
} |
||||
|
||||
// Number of leading zeros is mapped to the exponent: 2^-clz
|
||||
int clz = base_internal::CountLeadingZeros64(bits); |
||||
// Shift number left to erase leading zeros.
|
||||
bits <<= IncludeZero ? clz : (clz & 63); |
||||
// Shift number right to remove bits that overflow double mantissa. The
|
||||
// direction of the shift depends on `clz`.
|
||||
bits >>= (64 - FLT_MANT_DIG); |
||||
|
||||
// Construct IEEE 754 float exponent.
|
||||
// In the Signed case, bits is a 63-bit number with a 0 msb. Adjust the
|
||||
// exponent to account for that.
|
||||
const uint32_t exp = |
||||
(std::is_same<Signed, SignedValueT>::value ? 127U : 126U) + |
||||
static_cast<uint32_t>(ExponentBias - clz); |
||||
constexpr int kExp = FLT_MANT_DIG - 1; |
||||
const uint32_t val = sign | (exp << kExp) | (bits & ((1U << kExp) - 1U)); |
||||
|
||||
float res; |
||||
static_assert(sizeof(res) == sizeof(val), "float is not 32 bit"); |
||||
// Assumes that endian-ness is same for float and uint32_t.
|
||||
std::memcpy(&res, &val, sizeof(res)); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
template <typename Result> |
||||
struct RandU64ToReal { |
||||
template <typename Signed, bool IncludeZero, int ExponentBias = 0> |
||||
static inline Result Value(uint64_t bits) { |
||||
return RandU64ToDouble<Signed, IncludeZero, ExponentBias>(bits); |
||||
} |
||||
}; |
||||
|
||||
template <> |
||||
struct RandU64ToReal<float> { |
||||
template <typename Signed, bool IncludeZero, int ExponentBias = 0> |
||||
static inline float Value(uint64_t bits) { |
||||
return RandU64ToFloat<Signed, IncludeZero, ExponentBias>(bits); |
||||
} |
||||
}; |
||||
|
||||
} // namespace random_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_DISTRIBUTION_IMPL_H_
|
@ -0,0 +1,144 @@ |
||||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ |
||||
#define ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_ |
||||
|
||||
// This file contains some implementation details which are used by one or more
|
||||
// of the absl random number distributions.
|
||||
|
||||
#include <cstdint> |
||||
#include <cstring> |
||||
#include <limits> |
||||
#include <type_traits> |
||||
|
||||
#include "absl/base/internal/bits.h" |
||||
#include "absl/meta/type_traits.h" |
||||
#include "absl/random/internal/fastmath.h" |
||||
#include "absl/random/internal/traits.h" |
||||
|
||||
namespace absl { |
||||
namespace random_internal { |
||||
|
||||
// Tristate tag types controlling the output of GenerateRealFromBits.
|
||||
struct GeneratePositiveTag {}; |
||||
struct GenerateNegativeTag {}; |
||||
struct GenerateSignedTag {}; |
||||
|
||||
// GenerateRealFromBits generates a single real value from a single 64-bit
|
||||
// `bits` with template fields controlling the output.
|
||||
//
|
||||
// The `SignedTag` parameter controls whether positive, negative,
|
||||
// or either signed/unsigned may be returned.
|
||||
// When SignedTag == GeneratePositiveTag, range is U(0, 1)
|
||||
// When SignedTag == GenerateNegativeTag, range is U(-1, 0)
|
||||
// When SignedTag == GenerateSignedTag, range is U(-1, 1)
|
||||
//
|
||||
// When the `IncludeZero` parameter is true, the function may return 0 for some
|
||||
// inputs, otherwise it never returns 0.
|
||||
//
|
||||
// When a value in U(0,1) is required, use:
|
||||
// Uniform64ToReal<double, PositiveValueT, true>;
|
||||
//
|
||||
// When a value in U(-1,1) is required, use:
|
||||
// Uniform64ToReal<double, SignedValueT, false>;
|
||||
//
|
||||
// This generates more distinct values than the mathematical equivalent
|
||||
// `U(0, 1) * 2.0 - 1.0`.
|
||||
//
|
||||
// Scaling the result by powers of 2 (and avoiding a multiply) is also possible:
|
||||
// GenerateRealFromBits<double>(..., -1); => U(0, 0.5)
|
||||
// GenerateRealFromBits<double>(..., 1); => U(0, 2)
|
||||
//
|
||||
template <typename RealType, // Real type, either float or double.
|
||||
typename SignedTag = GeneratePositiveTag, // Whether a positive,
|
||||
// negative, or signed
|
||||
// value is generated.
|
||||
bool IncludeZero = true> |
||||
inline RealType GenerateRealFromBits(uint64_t bits, int exp_bias = 0) { |
||||
using real_type = RealType; |
||||
using uint_type = absl::conditional_t<std::is_same<real_type, float>::value, |
||||
uint32_t, uint64_t>; |
||||
|
||||
static_assert( |
||||
(std::is_same<double, real_type>::value || |
||||
std::is_same<float, real_type>::value), |
||||
"GenerateRealFromBits must be parameterized by either float or double."); |
||||
|
||||
static_assert(sizeof(uint_type) == sizeof(real_type), |
||||
"Mismatched unsinged and real types."); |
||||
|
||||
static_assert((std::numeric_limits<real_type>::is_iec559 && |
||||
std::numeric_limits<real_type>::radix == 2), |
||||
"RealType representation is not IEEE 754 binary."); |
||||
|
||||
static_assert((std::is_same<SignedTag, GeneratePositiveTag>::value || |
||||
std::is_same<SignedTag, GenerateNegativeTag>::value || |
||||
std::is_same<SignedTag, GenerateSignedTag>::value), |
||||
""); |
||||
|
||||
static constexpr int kExp = std::numeric_limits<real_type>::digits - 1; |
||||
static constexpr uint_type kMask = (static_cast<uint_type>(1) << kExp) - 1u; |
||||
static constexpr int kUintBits = sizeof(uint_type) * 8; |
||||
|
||||
int exp = exp_bias + int{std::numeric_limits<real_type>::max_exponent - 2}; |
||||
|
||||
// Determine the sign bit.
|
||||
// Depending on the SignedTag, this may use the left-most bit
|
||||
// or it may be a constant value.
|
||||
uint_type sign = std::is_same<SignedTag, GenerateNegativeTag>::value |
||||
? (static_cast<uint_type>(1) << (kUintBits - 1)) |
||||
: 0; |
||||
if (std::is_same<SignedTag, GenerateSignedTag>::value) { |
||||
if (std::is_same<uint_type, uint64_t>::value) { |
||||
sign = bits & uint64_t{0x8000000000000000}; |
||||
} |
||||
if (std::is_same<uint_type, uint32_t>::value) { |
||||
const uint64_t tmp = bits & uint64_t{0x8000000000000000}; |
||||
sign = static_cast<uint32_t>(tmp >> 32); |
||||
} |
||||
// adjust the bits and the exponent to account for removing
|
||||
// the leading bit.
|
||||
bits = bits & uint64_t{0x7FFFFFFFFFFFFFFF}; |
||||
exp++; |
||||
} |
||||
if (IncludeZero) { |
||||
if (bits == 0u) return 0; |
||||
} |
||||
|
||||
// Number of leading zeros is mapped to the exponent: 2^-clz
|
||||
// bits is 0..01xxxxxx. After shifting, we're left with 1xxx...0..0
|
||||
int clz = base_internal::CountLeadingZeros64(bits); |
||||
bits <<= (IncludeZero ? clz : (clz & 63)); // remove 0-bits.
|
||||
exp -= clz; // set the exponent.
|
||||
bits >>= (63 - kExp); |
||||
|
||||
// Construct the 32-bit or 64-bit IEEE 754 floating-point value from
|
||||
// the individual fields: sign, exp, mantissa(bits).
|
||||
uint_type val = |
||||
(std::is_same<SignedTag, GeneratePositiveTag>::value ? 0u : sign) | |
||||
(static_cast<uint_type>(exp) << kExp) | |
||||
(static_cast<uint_type>(bits) & kMask); |
||||
|
||||
// bit_cast to the output-type
|
||||
real_type result; |
||||
memcpy(static_cast<void*>(&result), static_cast<const void*>(&val), |
||||
sizeof(result)); |
||||
return result; |
||||
} |
||||
|
||||
} // namespace random_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_RANDOM_INTERNAL_GENERATE_REAL_H_
|
Loading…
Reference in new issue