uniform_real_distribution.h (7348B)
1 // Copyright 2017 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // ----------------------------------------------------------------------------- 16 // File: uniform_real_distribution.h 17 // ----------------------------------------------------------------------------- 18 // 19 // This header defines a class for representing a uniform floating-point 20 // distribution over a half-open interval [a,b). You use this distribution in 21 // combination with an Abseil random bit generator to produce random values 22 // according to the rules of the distribution. 23 // 24 // `absl::uniform_real_distribution` is a drop-in replacement for the C++11 25 // `std::uniform_real_distribution` [rand.dist.uni.real] but is considerably 26 // faster than the libstdc++ implementation. 27 // 28 // Note: the standard-library version may occasionally return `1.0` when 29 // default-initialized. See https://bugs.llvm.org//show_bug.cgi?id=18767 30 // `absl::uniform_real_distribution` does not exhibit this behavior. 31 32 #ifndef ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ 33 #define ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_ 34 35 #include <cassert> 36 #include <cmath> 37 #include <cstdint> 38 #include <istream> 39 #include <limits> 40 #include <ostream> 41 #include <type_traits> 42 43 #include "absl/base/config.h" 44 #include "absl/meta/type_traits.h" 45 #include "absl/random/internal/fast_uniform_bits.h" 46 #include "absl/random/internal/generate_real.h" 47 #include "absl/random/internal/iostream_state_saver.h" 48 49 namespace absl { 50 ABSL_NAMESPACE_BEGIN 51 52 // absl::uniform_real_distribution<T> 53 // 54 // This distribution produces random floating-point values uniformly distributed 55 // over the half-open interval [a, b). 56 // 57 // Example: 58 // 59 // absl::BitGen gen; 60 // 61 // // Use the distribution to produce a value between 0.0 (inclusive) 62 // // and 1.0 (exclusive). 63 // double value = absl::uniform_real_distribution<double>(0, 1)(gen); 64 // 65 template <typename RealType = double> 66 class uniform_real_distribution { 67 public: 68 using result_type = RealType; 69 70 class param_type { 71 public: 72 using distribution_type = uniform_real_distribution; 73 74 explicit param_type(result_type lo = 0, result_type hi = 1) 75 : lo_(lo), hi_(hi), range_(hi - lo) { 76 // [rand.dist.uni.real] preconditions 2 & 3 77 assert(lo <= hi); 78 79 // NOTE: For integral types, we can promote the range to an unsigned type, 80 // which gives full width of the range. However for real (fp) types, this 81 // is not possible, so value generation cannot use the full range of the 82 // real type. 83 assert(range_ <= (std::numeric_limits<result_type>::max)()); 84 } 85 86 result_type a() const { return lo_; } 87 result_type b() const { return hi_; } 88 89 friend bool operator==(const param_type& a, const param_type& b) { 90 return a.lo_ == b.lo_ && a.hi_ == b.hi_; 91 } 92 93 friend bool operator!=(const param_type& a, const param_type& b) { 94 return !(a == b); 95 } 96 97 private: 98 friend class uniform_real_distribution; 99 result_type lo_, hi_, range_; 100 101 static_assert(std::is_floating_point<RealType>::value, 102 "Class-template absl::uniform_real_distribution<> must be " 103 "parameterized using a floating-point type."); 104 }; 105 106 uniform_real_distribution() : uniform_real_distribution(0) {} 107 108 explicit uniform_real_distribution(result_type lo, result_type hi = 1) 109 : param_(lo, hi) {} 110 111 explicit uniform_real_distribution(const param_type& param) : param_(param) {} 112 113 // uniform_real_distribution<T>::reset() 114 // 115 // Resets the uniform real distribution. Note that this function has no effect 116 // because the distribution already produces independent values. 117 void reset() {} 118 119 template <typename URBG> 120 result_type operator()(URBG& gen) { // NOLINT(runtime/references) 121 return operator()(gen, param_); 122 } 123 124 template <typename URBG> 125 result_type operator()(URBG& gen, // NOLINT(runtime/references) 126 const param_type& p); 127 128 result_type a() const { return param_.a(); } 129 result_type b() const { return param_.b(); } 130 131 param_type param() const { return param_; } 132 void param(const param_type& params) { param_ = params; } 133 134 result_type(min)() const { return a(); } 135 result_type(max)() const { return b(); } 136 137 friend bool operator==(const uniform_real_distribution& a, 138 const uniform_real_distribution& b) { 139 return a.param_ == b.param_; 140 } 141 friend bool operator!=(const uniform_real_distribution& a, 142 const uniform_real_distribution& b) { 143 return a.param_ != b.param_; 144 } 145 146 private: 147 param_type param_; 148 random_internal::FastUniformBits<uint64_t> fast_u64_; 149 }; 150 151 // ----------------------------------------------------------------------------- 152 // Implementation details follow 153 // ----------------------------------------------------------------------------- 154 template <typename RealType> 155 template <typename URBG> 156 typename uniform_real_distribution<RealType>::result_type 157 uniform_real_distribution<RealType>::operator()( 158 URBG& gen, const param_type& p) { // NOLINT(runtime/references) 159 using random_internal::GeneratePositiveTag; 160 using random_internal::GenerateRealFromBits; 161 using real_type = 162 absl::conditional_t<std::is_same<RealType, float>::value, float, double>; 163 164 while (true) { 165 const result_type sample = 166 GenerateRealFromBits<real_type, GeneratePositiveTag, true>( 167 fast_u64_(gen)); 168 const result_type res = p.a() + (sample * p.range_); 169 if (res < p.b() || p.range_ <= 0 || !std::isfinite(p.range_)) { 170 return res; 171 } 172 // else sample rejected, try again. 173 } 174 } 175 176 template <typename CharT, typename Traits, typename RealType> 177 std::basic_ostream<CharT, Traits>& operator<<( 178 std::basic_ostream<CharT, Traits>& os, // NOLINT(runtime/references) 179 const uniform_real_distribution<RealType>& x) { 180 auto saver = random_internal::make_ostream_state_saver(os); 181 os.precision(random_internal::stream_precision_helper<RealType>::kPrecision); 182 os << x.a() << os.fill() << x.b(); 183 return os; 184 } 185 186 template <typename CharT, typename Traits, typename RealType> 187 std::basic_istream<CharT, Traits>& operator>>( 188 std::basic_istream<CharT, Traits>& is, // NOLINT(runtime/references) 189 uniform_real_distribution<RealType>& x) { // NOLINT(runtime/references) 190 using param_type = typename uniform_real_distribution<RealType>::param_type; 191 using result_type = typename uniform_real_distribution<RealType>::result_type; 192 auto saver = random_internal::make_istream_state_saver(is); 193 auto a = random_internal::read_floating_point<result_type>(is); 194 if (is.fail()) return is; 195 auto b = random_internal::read_floating_point<result_type>(is); 196 if (!is.fail()) { 197 x.param(param_type(a, b)); 198 } 199 return is; 200 } 201 ABSL_NAMESPACE_END 202 } // namespace absl 203 204 #endif // ABSL_RANDOM_UNIFORM_REAL_DISTRIBUTION_H_