// 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. // // ----------------------------------------------------------------------------- // File: uniform_real_distribution.h // ----------------------------------------------------------------------------- // // This header defines a class for representing a uniform floating-point // distribution over a half-open interval [a,b). You use this distribution in // combination with an Abseil random bit generator to produce random values // according to the rules of the distribution. // // `absl::uniform_real_distribution` is a drop-in replacement for the C++11 // `std::uniform_real_distribution` [rand.dist.uni.real] but is considerably // faster than the libstdc++ implementation. // // Note: the standard-library version may occasionally return `1.0` when // default-initialized. See https://bugs.llvm.org//show_bug.cgi?id=18767 // `absl::uniform_real_distribution` does not exhibit this behavior. #ifndef ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ #define ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ #include <cassert> #include <cmath> #include <cstdint> #include <istream> #include <limits> #include <type_traits> #include "absl/meta/type_traits.h" #include "absl/random/internal/fast_uniform_bits.h" #include "absl/random/internal/generate_real.h" #include "absl/random/internal/iostream_state_saver.h" namespace absl { ABSL_NAMESPACE_BEGIN // absl::uniform_real_distribution<T> // // This distribution produces random floating-point values uniformly distributed // over the half-open interval [a, b). // // Example: // // absl::BitGen gen; // // // Use the distribution to produce a value between 0.0 (inclusive) // // and 1.0 (exclusive). // double value = absl::uniform_real_distribution<double>(0, 1)(gen); // template <typename RealType = double> class uniform_real_distribution { public: using result_type = RealType; class param_type { public: using distribution_type = uniform_real_distribution; explicit param_type(result_type lo = 0, result_type hi = 1) : lo_(lo), hi_(hi), range_(hi - lo) { // [rand.dist.uni.real] preconditions 2 & 3 assert(lo <= hi); // NOTE: For integral types, we can promote the range to an unsigned type, // which gives full width of the range. However for real (fp) types, this // is not possible, so value generation cannot use the full range of the // real type. assert(range_ <= (std::numeric_limits<result_type>::max)()); assert(std::isfinite(range_)); } result_type a() const { return lo_; } result_type b() const { return hi_; } friend bool operator==(const param_type& a, const param_type& b) { return a.lo_ == b.lo_ && a.hi_ == b.hi_; } friend bool operator!=(const param_type& a, const param_type& b) { return !(a == b); } private: friend class uniform_real_distribution; result_type lo_, hi_, range_; static_assert(std::is_floating_point<RealType>::value, "Class-template absl::uniform_real_distribution<> must be " "parameterized using a floating-point type."); }; uniform_real_distribution() : uniform_real_distribution(0) {} explicit uniform_real_distribution(result_type lo, result_type hi = 1) : param_(lo, hi) {} explicit uniform_real_distribution(const param_type& param) : param_(param) {} // uniform_real_distribution<T>::reset() // // Resets the uniform real distribution. Note that this function has no effect // because the distribution already produces independent values. void reset() {} template <typename URBG> result_type operator()(URBG& gen) { // NOLINT(runtime/references) return operator()(gen, param_); } template <typename URBG> result_type operator()(URBG& gen, // NOLINT(runtime/references) const param_type& p); result_type a() const { return param_.a(); } result_type b() const { return param_.b(); } param_type param() const { return param_; } void param(const param_type& params) { param_ = params; } result_type(min)() const { return a(); } result_type(max)() const { return b(); } friend bool operator==(const uniform_real_distribution& a, const uniform_real_distribution& b) { return a.param_ == b.param_; } friend bool operator!=(const uniform_real_distribution& a, const uniform_real_distribution& b) { return a.param_ != b.param_; } private: param_type param_; random_internal::FastUniformBits<uint64_t> fast_u64_; }; // ----------------------------------------------------------------------------- // Implementation details follow // ----------------------------------------------------------------------------- template <typename RealType> template <typename URBG> typename uniform_real_distribution<RealType>::result_type uniform_real_distribution<RealType>::operator()( URBG& gen, const param_type& p) { // NOLINT(runtime/references) using random_internal::GeneratePositiveTag; using random_internal::GenerateRealFromBits; using real_type = absl::conditional_t<std::is_same<RealType, float>::value, float, double>; while (true) { const result_type sample = GenerateRealFromBits<real_type, GeneratePositiveTag, true>( fast_u64_(gen)); const result_type res = p.a() + (sample * p.range_); if (res < p.b() || p.range_ <= 0 || !std::isfinite(p.range_)) { return res; } // else sample rejected, try again. } } template <typename CharT, typename Traits, typename RealType> std::basic_ostream<CharT, Traits>& operator<<( std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) const uniform_real_distribution<RealType>& x) { auto saver = random_internal::make_ostream_state_saver(os); os.precision(random_internal::stream_precision_helper<RealType>::kPrecision); os << x.a() << os.fill() << x.b(); return os; } template <typename CharT, typename Traits, typename RealType> std::basic_istream<CharT, Traits>& operator>>( std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) uniform_real_distribution<RealType>& x) { // NOLINT(runtime/references) using param_type = typename uniform_real_distribution<RealType>::param_type; using result_type = typename uniform_real_distribution<RealType>::result_type; auto saver = random_internal::make_istream_state_saver(is); auto a = random_internal::read_floating_point<result_type>(is); if (is.fail()) return is; auto b = random_internal::read_floating_point<result_type>(is); if (!is.fail()) { x.param(param_type(a, b)); } return is; } ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_