-- 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